[llvm] [RISCV] Introduce a new syntax for processor-specific tuning feature strings (Take 2) (PR #175063)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 8 12:30:18 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

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

Author: Min-Yih Hsu (mshockwave)

<details>
<summary>Changes</summary>

(This is a second take of #<!-- -->168160 , as I made lots of changes and the direction of this feature has slightly shifted ever since)

This patch proposes new a tuning feature string format that helps users to build a performance model by "configuring" an existing tune CPU, along with its scheduling model. For example, this string
```
"sifive-x280:single-element-vec-fp64"
```
takes ``sifive-x280`` as the "base" tune CPU and configured it with ``single-element-vec-fp64``. This gives us a performance model that looks exactly like that of ``sifive-x280``, except some of the 64-bit vector floating point instructions now produce only a single element per cycle due to ``single-element-vec-fp64``. 

This string could eventually be used in places like ``-mtune`` at the frontend. Right now, this patch only implements the parser part, which is put under the TargetParser library.

The grammar for this string is:
```
    tune-cpu      ::= 'tuning CPU name in lower case'
    directive     ::= "[a-zA-Z0-9_-]+"
    tune-features ::= directive ["," directive]*
```
A *directive* can and can only _enable_ or _disable_ a certain tuning feature from the tuning CPU. A **positive directive**, like the ``single-element-vec-fp64`` we just saw, enables an additional tuning feature in the associated tuning model. 

A **negative directive**, on the other hand, removes a certain tuning feature. For example, ``sifive-x390`` already has the ``single-element-vec-fp64`` feature, and we can use "sifive-x390:no-single-element-vec-fp64" to create a new performance model that looks nearly the same as ``sifive-x390`` except ``single-element-vec-fp64`` being cut out. In this case, ``no-single-element-vec-fp64`` is a negative directive.

There are additional restrictions on what we can put in the list of directives, please refer to the documentations for more details.

Right now, this string only accepts directives that are explicitly supported by the tune CPU. For example, "sifive-x280:prefer-w-inst" is not a valide string as ``prefer-w-inst`` is not supported by ``sifive-x280`` at this moment. Vendors of these processors are expected to maintain the compatibility of their supported directives across different versions.

-----

The biggest differences with #<!-- -->168160 is that, per our previous discussions, we're tabling "generic" tuning features for now and only allow features that are explicitly supported by each processors. There are also some major TableGen changes so that instead of writing
```
def TuneFoo : SubtargetFeature<...>, RISCVSimpleTuneFeature;
```
We're now writing
```
def TuneFoo : RISCVSimpleTuneFeature<...>;
```
In addition to this, each processor now has to specify a list of accepted features in their TableGen definitions.



---

Patch is 43.38 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/175063.diff


8 Files Affected:

- (modified) llvm/docs/RISCVUsage.rst (+35) 
- (modified) llvm/include/llvm/TargetParser/RISCVTargetParser.h (+26) 
- (modified) llvm/lib/Target/RISCV/RISCVFeatures.td (+96-49) 
- (modified) llvm/lib/Target/RISCV/RISCVProcessors.td (+12-5) 
- (modified) llvm/lib/TargetParser/RISCVTargetParser.cpp (+205) 
- (modified) llvm/test/TableGen/riscv-target-def.td (+32-1) 
- (modified) llvm/unittests/TargetParser/RISCVTargetParserTest.cpp (+131) 
- (modified) llvm/utils/TableGen/Basic/RISCVTargetDefEmitter.cpp (+119-1) 


``````````diff
diff --git a/llvm/docs/RISCVUsage.rst b/llvm/docs/RISCVUsage.rst
index df5a595e8ec93..a94a21c57cd5a 100644
--- a/llvm/docs/RISCVUsage.rst
+++ b/llvm/docs/RISCVUsage.rst
@@ -610,3 +610,38 @@ Clang's ``-msmall-data-limit=`` option controls what the threshold size is (in b
 The small data limit threshold is also used to separate small constants into sections with names starting with ``.srodata``. LLD does not place these with the ``.sdata`` and ``.sbss`` sections as ``.srodata`` sections are read only and the other two are writable. Instead the ``.srodata`` sections are placed adjacent to ``.rodata``.
 
 Data suggests that these options can produce significant improvements across a range of benchmarks.
+
+Processor-Specific Tuning Feature String
+========================================
+Due to RISC-V's highly configurable nature, it is often desirable to share a single scheduling model across multiple similar RISC-V processors that only differ in a small number of (uArch) tuning features. An example of such tuning feature could be whether the latency of vector operations depend on VL or not. This could be extended to tuning features that are not directly connected to scheduling model but other parts of the RISC-V backend, like the cost of ``vrgather.vv`` instruction.
+
+To that end, RISC-V LLVM supports a tuning feature string format that helps users to build a performance model by "configuring" an existing tune CPU, along with its scheduling model. For example, this string
+
+::
+    "sifive-x280:single-element-vec-fp64"
+
+takes ``sifive-x280`` as the "base" tune CPU and configured it with ``single-element-vec-fp64``. This gives us a performance model that looks exactly like that of ``sifive-x280``, except some of the 64-bit vector floating point instructions now produce only a single element per cycle due to ``single-element-vec-fp64``. This string could eventually be used in places like ``-mtune`` at the frontend.
+
+More formally speaking, each tuning feature string has the following format:
+
+::
+    tune-cpu      ::= 'tuning CPU name in lower case'
+    directive     ::= "[a-zA-Z0-9\_-]+"
+    tune-features ::= directive ["," directive]*
+
+A *directive* can and can only _enable_ or _disable_ a certain tuning feature from the tuning CPU. A **positive directive**, like the ``single-element-vec-fp64`` we just saw, enables an additional tuning feature in the associated tuning model. A **negative directive**, on the other hand, removes a certain tuning feature. For example, ``sifive-x390`` already has the ``single-element-vec-fp64`` feature, and we can use
+
+::
+    "sifive-x390:no-single-element-vec-fp64"
+
+to create a new performance model that looks nearly the same as ``sifive-x390`` except ``single-element-vec-fp64`` being cut out. In this case, ``no-single-element-vec-fp64`` is a negative directive.
+
+There are some rules for the list of directives, though:
+
+1. The same directive cannot appear more than once.
+
+2. The positive and negative directives that belong to the same feature cannot appear at the same time.
+
+3. If a feature implies other features -- for example, ``short-forward-branch-imul`` implies ``short-forward-branch-ialu`` -- then the _implied_ features are subject to the previous two rules, too. For example, we cannot write _"short-forward-branch-imul,no-short-forward-branch-ialu"_, because the feature implied by ``short-forward-branch-imul`` violates rule 2.
+
+In addition to the rules listed above, right now, this string only accepts directives that are explicitly supported by the tune CPU. For example, _"sifive-x280:prefer-w-inst"_ is not a valide string as ``prefer-w-inst`` is not supported by ``sifive-x280`` at this moment. Vendors of these processors are expected to maintain the compatibility of their supported directives across different versions. There have been lots of discussions on having "generic" features that are universally supported by all RISC-V CPUs, yet many concerns -- including the difficulty to maintain compatibility across _all_ CPU targets and versions -- make us decide to table this issue until we find a reliable process to select such features.
diff --git a/llvm/include/llvm/TargetParser/RISCVTargetParser.h b/llvm/include/llvm/TargetParser/RISCVTargetParser.h
index 0b4c45e445bb6..b0ed9efcb9d82 100644
--- a/llvm/include/llvm/TargetParser/RISCVTargetParser.h
+++ b/llvm/include/llvm/TargetParser/RISCVTargetParser.h
@@ -16,6 +16,7 @@
 
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Compiler.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -47,6 +48,22 @@ struct CPUInfo {
   bool is64Bit() const { return DefaultMarch.starts_with("rv64"); }
 };
 
+/// Fatal errors encountered during parsing.
+struct ParserError : public ErrorInfo<ParserError, StringError> {
+  using ErrorInfo<ParserError, StringError>::ErrorInfo;
+  explicit ParserError(const Twine &S)
+      : ErrorInfo(S, inconvertibleErrorCode()) {}
+  static char ID;
+};
+
+/// Warnings encountered during parsing.
+struct ParserWarning : public ErrorInfo<ParserWarning, StringError> {
+  using ErrorInfo<ParserWarning, StringError>::ErrorInfo;
+  explicit ParserWarning(const Twine &S)
+      : ErrorInfo(S, inconvertibleErrorCode()) {}
+  static char ID;
+};
+
 // We use 64 bits as the known part in the scalable vector types.
 static constexpr unsigned RVVBitsPerBlock = 64;
 static constexpr unsigned RVVBytesPerBlock = RVVBitsPerBlock / 8;
@@ -54,6 +71,15 @@ static constexpr unsigned RVVBytesPerBlock = RVVBitsPerBlock / 8;
 LLVM_ABI void getFeaturesForCPU(StringRef CPU,
                                 SmallVectorImpl<std::string> &EnabledFeatures,
                                 bool NeedPlus = false);
+LLVM_ABI void getAllTuneFeatures(SmallVectorImpl<StringRef> &TuneFeatures);
+LLVM_ABI void
+getCPUConfigurableTuneFeatures(StringRef CPU,
+                               SmallVectorImpl<StringRef> &Directives);
+/// Parse the tune feature string with the respective processor. If \p ProcName
+/// is empty, directives are not filtered by processor.
+LLVM_ABI Error
+parseTuneFeatureString(StringRef ProcName, StringRef TFString,
+                       SmallVectorImpl<std::string> &TuneFeatures);
 LLVM_ABI bool parseCPU(StringRef CPU, bool IsRV64);
 LLVM_ABI bool parseTuneCPU(StringRef CPU, bool IsRV64);
 LLVM_ABI StringRef getMArchFromMcpu(StringRef CPU);
diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index 66e1b18f37392..33f6fb7eab8f1 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -1734,6 +1734,21 @@ def HasVendorXSMTVDot
 // LLVM specific features and extensions
 //===----------------------------------------------------------------------===//
 
+class RISCVTuneFeature<string name, string pos_directive, string neg_directive,
+                       string field_name, string value, string description,
+                       list<RISCVTuneFeature> implied_features = []>
+    : SubtargetFeature<name, field_name, value, description> {
+  let Implies = implied_features;
+
+  string PositiveDirectiveName = pos_directive;
+  string NegativeDirectiveName = neg_directive;
+}
+class RISCVSimpleTuneFeature<string name, string field_name, string value,
+                             string description,
+                             list<RISCVTuneFeature> implied_features = []>
+    : RISCVTuneFeature<name, name, "no-"#name, field_name, value, description,
+                       implied_features>;
+
 // Feature32Bit exists to mark CPUs that support RV32 to distinguish them from
 // tuning CPU names.
 def Feature32Bit
@@ -1824,28 +1839,50 @@ def TuneNLogNVRGather
    : SubtargetFeature<"log-vrgather", "RISCVVRGatherCostModel", "NLog2N",
                       "Has vrgather.vv with LMUL*log2(LMUL) latency">;
 
-def TunePostRAScheduler : SubtargetFeature<"use-postra-scheduler",
-    "UsePostRAScheduler", "true", "Schedule again after register allocation">;
-
-def TuneDisableMISchedLoadClustering : SubtargetFeature<"disable-misched-load-clustering",
-    "EnableMISchedLoadClustering", "false", "Disable load clustering in the machine scheduler">;
-
-def TuneDisableMISchedStoreClustering : SubtargetFeature<"disable-misched-store-clustering",
-    "EnableMISchedStoreClustering", "false", "Disable store clustering in the machine scheduler">;
-
-def TuneDisablePostMISchedLoadClustering : SubtargetFeature<"disable-postmisched-load-clustering",
-    "EnablePostMISchedLoadClustering", "false", "Disable PostRA load clustering in the machine scheduler">;
-
-def TuneDisablePostMISchedStoreClustering : SubtargetFeature<"disable-postmisched-store-clustering",
-    "EnablePostMISchedStoreClustering", "false", "Disable PostRA store clustering in the machine scheduler">;
+def TunePostRAScheduler
+    : RISCVSimpleTuneFeature<"use-postra-scheduler", "UsePostRAScheduler",
+                             "true",
+                             "Schedule again after register allocation">;
+
+def TuneDisableMISchedLoadClustering
+    : RISCVTuneFeature<
+          "disable-misched-load-clustering", "disable-misched-load-clustering",
+          "enable-misched-load-clustering", "EnableMISchedLoadClustering",
+          "false", "Disable load clustering in the machine scheduler">;
+
+def TuneDisableMISchedStoreClustering
+    : RISCVTuneFeature<"disable-misched-store-clustering",
+                       "disable-misched-store-clustering",
+                       "enable-misched-store-clustering",
+                       "EnableMISchedStoreClustering", "false",
+                       "Disable store clustering in the machine scheduler">;
+
+def TuneDisablePostMISchedLoadClustering
+    : RISCVTuneFeature<
+          "disable-postmisched-load-clustering",
+          "disable-postmisched-load-clustering",
+          "enable-postmisched-load-clustering",
+          "EnablePostMISchedLoadClustering", "false",
+          "Disable PostRA load clustering in the machine scheduler">;
+
+def TuneDisablePostMISchedStoreClustering
+    : RISCVTuneFeature<
+          "disable-postmisched-store-clustering",
+          "disable-postmisched-store-clustering",
+          "enable-postmisched-store-clustering",
+          "EnablePostMISchedStoreClustering", "false",
+          "Disable PostRA store clustering in the machine scheduler">;
 
 def TuneDisableLatencySchedHeuristic
-    : SubtargetFeature<"disable-latency-sched-heuristic", "DisableLatencySchedHeuristic", "true",
-                       "Disable latency scheduling heuristic">;
+    : RISCVTuneFeature<
+          "disable-latency-sched-heuristic", "disable-latency-sched-heuristic",
+          "enable-latency-sched-heuristic", "DisableLatencySchedHeuristic",
+          "true", "Disable latency scheduling heuristic">;
 
 def TunePredictableSelectIsExpensive
-    : SubtargetFeature<"predictable-select-expensive", "PredictableSelectIsExpensive", "true",
-                       "Prefer likely predicted branches over selects">;
+    : RISCVSimpleTuneFeature<"predictable-select-expensive",
+                             "PredictableSelectIsExpensive", "true",
+                             "Prefer likely predicted branches over selects">;
 
 def TuneOptimizedZeroStrideLoad
    : SubtargetFeature<"optimized-zero-stride-load", "HasOptimizedZeroStrideLoad",
@@ -1860,9 +1897,10 @@ foreach nf = {2-8} in
                        "implemented as a wide memory op and shuffle">;
 
 def TuneVLDependentLatency
-    : SubtargetFeature<"vl-dependent-latency", "HasVLDependentLatency", "true",
-                       "Latency of vector instructions is dependent on the "
-                       "dynamic value of vl">;
+    : RISCVSimpleTuneFeature<
+          "vl-dependent-latency", "HasVLDependentLatency", "true",
+          "Latency of vector instructions is dependent on the "
+          "dynamic value of vl">;
 
 def Experimental
    : SubtargetFeature<"experimental", "HasExperimental",
@@ -1876,7 +1914,8 @@ def TuneDLenFactor2
                       "Vector unit DLEN(data path width) is half of VLEN">;
 
 def TuneNoDefaultUnroll
-    : SubtargetFeature<"no-default-unroll", "EnableDefaultUnroll", "false",
+    : RISCVTuneFeature<"no-default-unroll", "no-default-unroll",
+                       "enable-default-unroll", "EnableDefaultUnroll", "false",
                        "Disable default unroll preference.">;
 
 // Many Microarchitectures are able to fuse a branch over a single instruction
@@ -1901,59 +1940,67 @@ def TuneNoDefaultUnroll
 // the vendor-specific instructions should only be enabled for vendor
 // cores.
 def TuneShortForwardBranchIALU
-    : SubtargetFeature<"short-forward-branch-ialu", "HasShortForwardBranchIALU",
-                       "true", "Enable short forward branch optimization for RVI base instructions">;
+    : RISCVSimpleTuneFeature<
+          "short-forward-branch-ialu", "HasShortForwardBranchIALU", "true",
+          "Enable short forward branch optimization for RVI base instructions">;
 def HasShortForwardBranchIALU : Predicate<"Subtarget->hasShortForwardBranchIALU()">;
 def NoShortForwardBranch : Predicate<"!Subtarget->hasShortForwardBranchIALU()">;
 
 def TuneShortForwardBranchIMinMax
-    : SubtargetFeature<"short-forward-branch-iminmax", "HasShortForwardBranchIMinMax",
-                       "true", "Enable short forward branch optimization for MIN,MAX instructions in Zbb",
-                       [TuneShortForwardBranchIALU]>;
+    : RISCVSimpleTuneFeature<"short-forward-branch-iminmax",
+                             "HasShortForwardBranchIMinMax", "true",
+                             "Enable short forward branch optimization for "
+                             "MIN,MAX instructions in Zbb",
+                             [TuneShortForwardBranchIALU]>;
 def HasShortForwardBranchIMinMax : Predicate<"Subtarget->hasShortForwardBranchIMinMax()">;
 
 def TuneShortForwardBranchIMul
-    : SubtargetFeature<"short-forward-branch-imul", "HasShortForwardBranchIMul",
-                       "true", "Enable short forward branch optimization for MUL instruction",
-                       [TuneShortForwardBranchIALU]>;
+    : RISCVSimpleTuneFeature<
+          "short-forward-branch-imul", "HasShortForwardBranchIMul", "true",
+          "Enable short forward branch optimization for MUL instruction",
+          [TuneShortForwardBranchIALU]>;
 def HasShortForwardBranchIMul : Predicate<"Subtarget->hasShortForwardBranchIMul()">;
 
 def TuneShortForwardBranchILoad
-    : SubtargetFeature<"short-forward-branch-iload", "HasShortForwardBranchILoad",
-                       "true", "Enable short forward branch optimization for load instructions",
-                       [TuneShortForwardBranchIALU]>;
+    : RISCVSimpleTuneFeature<
+          "short-forward-branch-iload", "HasShortForwardBranchILoad", "true",
+          "Enable short forward branch optimization for load instructions",
+          [TuneShortForwardBranchIALU]>;
 def HasShortForwardBranchILoad : Predicate<"Subtarget->hasShortForwardBranchILoad()">;
 
 // Some subtargets require a S2V transfer buffer to move scalars into vectors.
 // FIXME: Forming .vx/.vf/.wx/.wf can reduce register pressure.
 def TuneNoSinkSplatOperands
-    : SubtargetFeature<"no-sink-splat-operands", "SinkSplatOperands",
-                       "false", "Disable sink splat operands to enable .vx, .vf,"
-                       ".wx, and .wf instructions">;
+    : RISCVTuneFeature<"no-sink-splat-operands", "no-sink-splat-operands",
+                       "sink-splat-operands", "SinkSplatOperands", "false",
+                       "Disable sink splat operands to enable .vx, .vf, .wx, "
+                       "and .wf instructions">;
 
 def TunePreferWInst
-    : SubtargetFeature<"prefer-w-inst", "PreferWInst", "true",
-                       "Prefer instructions with W suffix">;
+    : RISCVSimpleTuneFeature<"prefer-w-inst", "PreferWInst", "true",
+                             "Prefer instructions with W suffix">;
 
 def TuneConditionalCompressedMoveFusion
-    : SubtargetFeature<"conditional-cmv-fusion", "HasConditionalCompressedMoveFusion",
-                       "true", "Enable branch+c.mv fusion">;
+    : RISCVSimpleTuneFeature<"conditional-cmv-fusion",
+                             "HasConditionalCompressedMoveFusion", "true",
+                             "Enable branch+c.mv fusion">;
 def HasConditionalMoveFusion : Predicate<"Subtarget->hasConditionalMoveFusion()">;
 def NoConditionalMoveFusion  : Predicate<"!Subtarget->hasConditionalMoveFusion()">;
 
 def TuneHasSingleElementVecFP64
-    : SubtargetFeature<"single-element-vec-fp64", "HasSingleElementVectorFP64", "true",
-                       "Certain vector FP64 operations produce a single result "
-                       "element per cycle">;
+    : RISCVSimpleTuneFeature<
+          "single-element-vec-fp64", "HasSingleElementVectorFP64", "true",
+          "Certain vector FP64 operations produce a single result "
+          "element per cycle">;
 
-def TuneVXRMPipelineFlush : SubtargetFeature<"vxrm-pipeline-flush", "HasVXRMPipelineFlush",
-                                             "true", "VXRM writes causes pipeline flush">;
+def TuneVXRMPipelineFlush
+    : RISCVSimpleTuneFeature<"vxrm-pipeline-flush", "HasVXRMPipelineFlush",
+                             "true", "VXRM writes causes pipeline flush">;
 
 def TunePreferVsetvliOverReadVLENB
-    : SubtargetFeature<"prefer-vsetvli-over-read-vlenb",
-                       "PreferVsetvliOverReadVLENB",
-                       "true",
-                       "Prefer vsetvli over read vlenb CSR to calculate VLEN">;
+    : RISCVSimpleTuneFeature<
+          "prefer-vsetvli-over-read-vlenb", "PreferVsetvliOverReadVLENB",
+          "true", "Prefer vsetvli over read vlenb CSR to calculate VLEN">;
 
 //===----------------------------------------------------------------------===//
 // CPU Families (alphabetized by vendor).
diff --git a/llvm/lib/Target/RISCV/RISCVProcessors.td b/llvm/lib/Target/RISCV/RISCVProcessors.td
index 5becfd2ad502b..f378f3c2888c7 100644
--- a/llvm/lib/Target/RISCV/RISCVProcessors.td
+++ b/llvm/lib/Target/RISCV/RISCVProcessors.td
@@ -78,13 +78,16 @@ class RISCVProcessorModel<string n,
   int MVendorID = 0;
   int MArchID = 0;
   int MImpID = 0;
+
+  list<RISCVTuneFeature> ConfigurableTuneFeatures = [];
 }
 
-class RISCVTuneProcessorModel<string n,
-                              SchedMachineModel m,
+class RISCVTuneProcessorModel<string n, SchedMachineModel m,
                               list<SubtargetFeature> tunef = [],
                               list<SubtargetFeature> f = []>
-    : ProcessorModel<n, m, f,tunef>;
+    : ProcessorModel<n, m, f, tunef> {
+  list<RISCVTuneFeature> ConfigurableTuneFeatures = [];
+}
 
 defvar GenericTuneFeatures = [TuneOptimizedNF2SegmentLoadStore];
 
@@ -293,7 +296,9 @@ def SIFIVE_X280 : RISCVProcessorModel<"sifive-x280", SiFive7Model,
                                        FeatureStdExtZvfh,
                                        FeatureStdExtZba,
                                        FeatureStdExtZbb],
-                                      SiFiveIntelligenceTuneFeatures>;
+                                      SiFiveIntelligenceTuneFeatures> {
+  let ConfigurableTuneFeatures = [TuneHasSingleElementVecFP64];
+}
 
 def SIFIVE_X390 : RISCVProcessorModel<"sifive-x390",
                                       SiFiveX390Model,
@@ -339,7 +344,9 @@ def SIFIVE_X390 : RISCVProcessorModel<"sifive-x390",
                                        FeatureVendorXSiFivecdiscarddlone,
                                        FeatureVendorXSiFivecflushdlone],
                                       !listconcat(SiFiveIntelligenceTuneFeatures,
-                                                  [TuneHasSingleElementVecFP64])>;
+                                                  [TuneHasSingleElementVecFP64])> {
+  let ConfigurableTuneFeatures = [TuneHasSingleElementVecFP64];
+}
 
 defvar SiFiveP400TuneFeatures = [TuneNoDefaultUnroll,
                                  TuneConditionalCompressedMoveFusion,
diff --git a/llvm/lib/TargetParser/RISCVTargetParser.cpp b/llvm/lib/TargetParser/RISCVTargetParser.cpp
index d0bd39f15afc4..eef426d7c9aee 100644
--- a/llvm/lib/TargetParser/RISCVTargetParser.cpp
+++ b/llvm/lib/TargetParser/RISCVTargetParser.cpp
@...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/175063


More information about the llvm-commits mailing list