[clang] [clang] Support `#pragma clang loop pipeline(enable)` (PR #112501)

Ryotaro Kasuga via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 16 01:51:38 PDT 2024


https://github.com/kasuga-fj created https://github.com/llvm/llvm-project/pull/112501

Previously `#pragma clang loop pipeline` only accepted `disable`. This patch adds `enable` as a valid argument for this pragma. This allows Software Pipelining optimization to be applied to some loops instead of all loops.

This is clang part of the fix.

>From 04f0f22178272dbf2ebe8a74569245f97a2f644b Mon Sep 17 00:00:00 2001
From: Ryotaro Kasuga <kasuga.ryotaro at fujitsu.com>
Date: Thu, 10 Oct 2024 09:08:00 +0000
Subject: [PATCH] [clang] Support `#pragma clang loop pipeline(enable)`

Previously `#pragma clang loop pipeline` only accepted `disable`. This
patch adds `enable` as a valid argument for this pragma. This allows
Software Pipelining optimization to be applied to some loops instead of
all loops.

This is clang part of the fix.
---
 clang/include/clang/Basic/Attr.td             |  6 ++--
 clang/include/clang/Basic/AttrDocs.td         | 12 ++++++-
 .../clang/Basic/DiagnosticParseKinds.td       |  2 --
 clang/lib/CodeGen/CGLoopInfo.cpp              | 36 ++++++++++++-------
 clang/lib/CodeGen/CGLoopInfo.h                | 11 +++---
 clang/lib/Parse/ParsePragma.cpp               | 34 ++++++++----------
 clang/lib/Sema/SemaStmtAttr.cpp               |  8 ++---
 clang/test/CodeGenCXX/pragma-pipeline.cpp     | 12 +++++++
 clang/test/Parser/pragma-loop.cpp             |  2 +-
 clang/test/Parser/pragma-pipeline.cpp         |  8 +++--
 10 files changed, 82 insertions(+), 49 deletions(-)

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index ec3d6e0079f630..3d5d5f6ca99f1e 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4190,7 +4190,7 @@ def LoopHint : Attr {
   /// unroll_and_jam: attempt to unroll and jam loop if State == Enable.
   /// unroll_and_jam_count: unroll and jams loop 'Value' times.
   /// distribute: attempt to distribute loop if State == Enable.
-  /// pipeline: disable pipelining loop if State == Disable.
+  /// pipeline: enable pipelining loop if State == Enable.
   /// pipeline_initiation_interval: create loop schedule with initiation interval equal to 'Value'.
 
   /// #pragma unroll <argument> directive
@@ -4210,7 +4210,7 @@ def LoopHint : Attr {
                            "vectorize_predicate"],
                           ["Vectorize", "VectorizeWidth", "Interleave", "InterleaveCount",
                            "Unroll", "UnrollCount", "UnrollAndJam", "UnrollAndJamCount",
-                           "PipelineDisabled", "PipelineInitiationInterval", "Distribute",
+                           "Pipeline", "PipelineInitiationInterval", "Distribute",
                            "VectorizePredicate"]>,
               EnumArgument<"State", "LoopHintState", /*is_string=*/false,
                            ["enable", "disable", "numeric", "fixed_width",
@@ -4230,7 +4230,7 @@ def LoopHint : Attr {
     case UnrollCount: return "unroll_count";
     case UnrollAndJam: return "unroll_and_jam";
     case UnrollAndJamCount: return "unroll_and_jam_count";
-    case PipelineDisabled: return "pipeline";
+    case Pipeline: return "pipeline";
     case PipelineInitiationInterval: return "pipeline_initiation_interval";
     case Distribute: return "distribute";
     case VectorizePredicate: return "vectorize_predicate";
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index b1512e22ee2dd4..e2591c7be7905a 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3968,8 +3968,18 @@ def PipelineHintDocs : Documentation {
   placed immediately before a for, while, do-while, or a C++11 range-based for
   loop.
 
+  Using ``#pragma clang loop pipeline(enable)`` applies the software pipelining
+  optimization if possible:
+
+  .. code-block:: c++
+
+  #pragma clang loop pipeline(enable)
+  for (...) {
+    ...
+  }
+
   Using ``#pragma clang loop pipeline(disable)`` avoids the software pipelining
-  optimization. The disable state can only be specified:
+  optimization:
 
   .. code-block:: c++
 
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 78510e61a639fa..1bf82923aa26bd 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1676,8 +1676,6 @@ def err_pragma_fp_invalid_argument : Error<
 
 def err_pragma_invalid_keyword : Error<
   "invalid argument; expected 'enable'%select{|, 'full'}0%select{|, 'assume_safety'}1 or 'disable'">;
-def err_pragma_pipeline_invalid_keyword : Error<
-    "invalid argument; expected 'disable'">;
 
 // API notes.
 def err_type_unparsed : Error<"unparsed tokens following type">;
diff --git a/clang/lib/CodeGen/CGLoopInfo.cpp b/clang/lib/CodeGen/CGLoopInfo.cpp
index 6b886bd6b6d2cf..04b229da2013e3 100644
--- a/clang/lib/CodeGen/CGLoopInfo.cpp
+++ b/clang/lib/CodeGen/CGLoopInfo.cpp
@@ -39,9 +39,10 @@ MDNode *LoopInfo::createPipeliningMetadata(const LoopAttributes &Attrs,
   LLVMContext &Ctx = Header->getContext();
 
   std::optional<bool> Enabled;
-  if (Attrs.PipelineDisabled)
+  if (Attrs.Pipeline == LoopAttributes::Disable)
     Enabled = false;
-  else if (Attrs.PipelineInitiationInterval != 0)
+  else if (Attrs.Pipeline == LoopAttributes::Enable ||
+           Attrs.PipelineInitiationInterval != 0)
     Enabled = true;
 
   if (Enabled != true) {
@@ -69,6 +70,11 @@ MDNode *LoopInfo::createPipeliningMetadata(const LoopAttributes &Attrs,
     Args.push_back(MDNode::get(Ctx, Vals));
   }
 
+  if (Attrs.Pipeline == LoopAttributes::Enable) {
+    Metadata *Vals[] = {MDString::get(Ctx, "llvm.loop.pipeline.enable")};
+    Args.push_back(MDNode::get(Ctx, Vals));
+  }
+
   // No follow-up: This is the last transformation.
 
   MDNode *LoopID = MDNode::getDistinct(Ctx, Args);
@@ -460,8 +466,9 @@ LoopAttributes::LoopAttributes(bool IsParallel)
       VectorizePredicateEnable(LoopAttributes::Unspecified), VectorizeWidth(0),
       VectorizeScalable(LoopAttributes::Unspecified), InterleaveCount(0),
       UnrollCount(0), UnrollAndJamCount(0),
-      DistributeEnable(LoopAttributes::Unspecified), PipelineDisabled(false),
-      PipelineInitiationInterval(0), CodeAlign(0), MustProgress(false) {}
+      DistributeEnable(LoopAttributes::Unspecified),
+      Pipeline(LoopAttributes::Unspecified), PipelineInitiationInterval(0),
+      CodeAlign(0), MustProgress(false) {}
 
 void LoopAttributes::clear() {
   IsParallel = false;
@@ -475,7 +482,7 @@ void LoopAttributes::clear() {
   UnrollAndJamEnable = LoopAttributes::Unspecified;
   VectorizePredicateEnable = LoopAttributes::Unspecified;
   DistributeEnable = LoopAttributes::Unspecified;
-  PipelineDisabled = false;
+  Pipeline = LoopAttributes::Unspecified;
   PipelineInitiationInterval = 0;
   CodeAlign = 0;
   MustProgress = false;
@@ -496,7 +503,8 @@ LoopInfo::LoopInfo(BasicBlock *Header, const LoopAttributes &Attrs,
   if (!Attrs.IsParallel && Attrs.VectorizeWidth == 0 &&
       Attrs.VectorizeScalable == LoopAttributes::Unspecified &&
       Attrs.InterleaveCount == 0 && Attrs.UnrollCount == 0 &&
-      Attrs.UnrollAndJamCount == 0 && !Attrs.PipelineDisabled &&
+      Attrs.UnrollAndJamCount == 0 &&
+      Attrs.Pipeline == LoopAttributes::Unspecified &&
       Attrs.PipelineInitiationInterval == 0 &&
       Attrs.VectorizePredicateEnable == LoopAttributes::Unspecified &&
       Attrs.VectorizeEnable == LoopAttributes::Unspecified &&
@@ -552,7 +560,7 @@ void LoopInfo::finish() {
 
     AfterJam.VectorizePredicateEnable = Attrs.VectorizePredicateEnable;
     AfterJam.UnrollCount = Attrs.UnrollCount;
-    AfterJam.PipelineDisabled = Attrs.PipelineDisabled;
+    AfterJam.Pipeline = Attrs.Pipeline;
     AfterJam.PipelineInitiationInterval = Attrs.PipelineInitiationInterval;
 
     // If this loop is subject of an unroll-and-jam by the parent loop, and has
@@ -680,8 +688,8 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
       case LoopHintAttr::Distribute:
         setDistributeState(false);
         break;
-      case LoopHintAttr::PipelineDisabled:
-        setPipelineDisabled(true);
+      case LoopHintAttr::Pipeline:
+        setPipelineEnable(false);
         break;
       case LoopHintAttr::UnrollCount:
       case LoopHintAttr::UnrollAndJamCount:
@@ -710,11 +718,13 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
       case LoopHintAttr::Distribute:
         setDistributeState(true);
         break;
+      case LoopHintAttr::Pipeline:
+        setPipelineEnable(true);
+        break;
       case LoopHintAttr::UnrollCount:
       case LoopHintAttr::UnrollAndJamCount:
       case LoopHintAttr::VectorizeWidth:
       case LoopHintAttr::InterleaveCount:
-      case LoopHintAttr::PipelineDisabled:
       case LoopHintAttr::PipelineInitiationInterval:
         llvm_unreachable("Options cannot enabled.");
         break;
@@ -736,7 +746,7 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
       case LoopHintAttr::VectorizeWidth:
       case LoopHintAttr::InterleaveCount:
       case LoopHintAttr::Distribute:
-      case LoopHintAttr::PipelineDisabled:
+      case LoopHintAttr::Pipeline:
       case LoopHintAttr::PipelineInitiationInterval:
         llvm_unreachable("Options cannot be used to assume mem safety.");
         break;
@@ -757,7 +767,7 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
       case LoopHintAttr::VectorizeWidth:
       case LoopHintAttr::InterleaveCount:
       case LoopHintAttr::Distribute:
-      case LoopHintAttr::PipelineDisabled:
+      case LoopHintAttr::Pipeline:
       case LoopHintAttr::PipelineInitiationInterval:
       case LoopHintAttr::VectorizePredicate:
         llvm_unreachable("Options cannot be used with 'full' hint.");
@@ -800,7 +810,7 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
       case LoopHintAttr::VectorizeWidth:
       case LoopHintAttr::Interleave:
       case LoopHintAttr::Distribute:
-      case LoopHintAttr::PipelineDisabled:
+      case LoopHintAttr::Pipeline:
         llvm_unreachable("Options cannot be assigned a value.");
         break;
       }
diff --git a/clang/lib/CodeGen/CGLoopInfo.h b/clang/lib/CodeGen/CGLoopInfo.h
index 0fe33b28913063..58d4ea6c45d2cb 100644
--- a/clang/lib/CodeGen/CGLoopInfo.h
+++ b/clang/lib/CodeGen/CGLoopInfo.h
@@ -73,8 +73,8 @@ struct LoopAttributes {
   /// Value for llvm.loop.distribute.enable metadata.
   LVEnableState DistributeEnable;
 
-  /// Value for llvm.loop.pipeline.disable metadata.
-  bool PipelineDisabled;
+  /// Value for llvm.loop.pipeline metadata.
+  LVEnableState Pipeline;
 
   /// Value for llvm.loop.pipeline.iicount metadata.
   unsigned PipelineInitiationInterval;
@@ -281,8 +281,11 @@ class LoopInfoStack {
   /// \brief Set the unroll count for the next loop pushed.
   void setUnrollAndJamCount(unsigned C) { StagedAttrs.UnrollAndJamCount = C; }
 
-  /// Set the pipeline disabled state.
-  void setPipelineDisabled(bool S) { StagedAttrs.PipelineDisabled = S; }
+  /// Set the next pushed loop as a pipeline candidate.
+  void setPipelineEnable(bool Enable = true) {
+    StagedAttrs.Pipeline =
+        Enable ? LoopAttributes::Enable : LoopAttributes::Disable;
+  }
 
   /// Set the pipeline initiation interval.
   void setPipelineInitiationInterval(unsigned C) {
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index 12fed448d477c0..7e9b80cd0b0a04 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -1454,24 +1454,24 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) {
   bool OptionUnroll = false;
   bool OptionUnrollAndJam = false;
   bool OptionDistribute = false;
-  bool OptionPipelineDisabled = false;
+  bool OptionPipeline = false;
   bool StateOption = false;
   if (OptionInfo) { // Pragma Unroll does not specify an option.
     OptionUnroll = OptionInfo->isStr("unroll");
     OptionUnrollAndJam = OptionInfo->isStr("unroll_and_jam");
     OptionDistribute = OptionInfo->isStr("distribute");
-    OptionPipelineDisabled = OptionInfo->isStr("pipeline");
+    OptionPipeline = OptionInfo->isStr("pipeline");
     StateOption = llvm::StringSwitch<bool>(OptionInfo->getName())
                       .Case("vectorize", true)
                       .Case("interleave", true)
                       .Case("vectorize_predicate", true)
                       .Default(false) ||
                   OptionUnroll || OptionUnrollAndJam || OptionDistribute ||
-                  OptionPipelineDisabled;
+                  OptionPipeline;
   }
 
   bool AssumeSafetyArg = !OptionUnroll && !OptionUnrollAndJam &&
-                         !OptionDistribute && !OptionPipelineDisabled;
+                         !OptionDistribute && !OptionPipeline;
   // Verify loop hint has an argument.
   if (Toks[0].is(tok::eof)) {
     ConsumeAnnotationToken();
@@ -1488,21 +1488,17 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) {
     SourceLocation StateLoc = Toks[0].getLocation();
     IdentifierInfo *StateInfo = Toks[0].getIdentifierInfo();
 
-    bool Valid = StateInfo &&
-                 llvm::StringSwitch<bool>(StateInfo->getName())
-                     .Case("disable", true)
-                     .Case("enable", !OptionPipelineDisabled)
-                     .Case("full", OptionUnroll || OptionUnrollAndJam)
-                     .Case("assume_safety", AssumeSafetyArg)
-                     .Default(false);
+    bool Valid =
+        StateInfo && llvm::StringSwitch<bool>(StateInfo->getName())
+                         .Case("disable", true)
+                         .Case("enable", true)
+                         .Case("full", OptionUnroll || OptionUnrollAndJam)
+                         .Case("assume_safety", AssumeSafetyArg)
+                         .Default(false);
     if (!Valid) {
-      if (OptionPipelineDisabled) {
-        Diag(Toks[0].getLocation(), diag::err_pragma_pipeline_invalid_keyword);
-      } else {
-        Diag(Toks[0].getLocation(), diag::err_pragma_invalid_keyword)
-            << /*FullKeyword=*/(OptionUnroll || OptionUnrollAndJam)
-            << /*AssumeSafetyKeyword=*/AssumeSafetyArg;
-      }
+      Diag(Toks[0].getLocation(), diag::err_pragma_invalid_keyword)
+          << /*FullKeyword=*/(OptionUnroll || OptionUnrollAndJam)
+          << /*AssumeSafetyKeyword=*/AssumeSafetyArg;
       return false;
     }
     if (Toks.size() > 2)
@@ -3591,7 +3587,7 @@ static bool ParseLoopHintValue(Preprocessor &PP, Token &Tok, Token PragmaName,
 ///    'vectorize_width' '(' loop-hint-value ')'
 ///    'interleave_count' '(' loop-hint-value ')'
 ///    'unroll_count' '(' loop-hint-value ')'
-///    'pipeline' '(' disable ')'
+///    'pipeline' '(' loop-hint-keyword ')'
 ///    'pipeline_initiation_interval' '(' loop-hint-value ')'
 ///
 ///  loop-hint-keyword:
diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index f801455596fe6f..9d2989cd12e021 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -142,7 +142,7 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
                  .Case("interleave_count", LoopHintAttr::InterleaveCount)
                  .Case("unroll", LoopHintAttr::Unroll)
                  .Case("unroll_count", LoopHintAttr::UnrollCount)
-                 .Case("pipeline", LoopHintAttr::PipelineDisabled)
+                 .Case("pipeline", LoopHintAttr::Pipeline)
                  .Case("pipeline_initiation_interval",
                        LoopHintAttr::PipelineInitiationInterval)
                  .Case("distribute", LoopHintAttr::Distribute)
@@ -170,7 +170,7 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
                Option == LoopHintAttr::VectorizePredicate ||
                Option == LoopHintAttr::Unroll ||
                Option == LoopHintAttr::Distribute ||
-               Option == LoopHintAttr::PipelineDisabled) {
+               Option == LoopHintAttr::Pipeline) {
       assert(StateLoc && StateLoc->Ident && "Loop hint must have an argument");
       if (StateLoc->Ident->isStr("disable"))
         State = LoopHintAttr::Disable;
@@ -516,7 +516,7 @@ CheckForIncompatibleAttributes(Sema &S,
       // Perform the check for duplicated 'distribute' hints.
       Category = Distribute;
       break;
-    case LoopHintAttr::PipelineDisabled:
+    case LoopHintAttr::Pipeline:
     case LoopHintAttr::PipelineInitiationInterval:
       Category = Pipeline;
       break;
@@ -532,7 +532,7 @@ CheckForIncompatibleAttributes(Sema &S,
         Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll ||
         Option == LoopHintAttr::UnrollAndJam ||
         Option == LoopHintAttr::VectorizePredicate ||
-        Option == LoopHintAttr::PipelineDisabled ||
+        Option == LoopHintAttr::Pipeline ||
         Option == LoopHintAttr::Distribute) {
       // Enable|Disable|AssumeSafety hint.  For example, vectorize(enable).
       PrevAttr = CategoryState.StateAttr;
diff --git a/clang/test/CodeGenCXX/pragma-pipeline.cpp b/clang/test/CodeGenCXX/pragma-pipeline.cpp
index b7d2136745c7d9..f8948adf1a9d8a 100644
--- a/clang/test/CodeGenCXX/pragma-pipeline.cpp
+++ b/clang/test/CodeGenCXX/pragma-pipeline.cpp
@@ -36,6 +36,15 @@ void pipeline_disabled_on_nested_loop(int *List, int Length, int Value) {
   }
 }
 
+void pipeline_enabled(int *List, int Length, int Value) {
+// CHECK-LABEL: define {{.*}} @_Z16pipeline_enabled
+#pragma clang loop pipeline(enable)
+  for (int i = 0; i < Length; i++) {
+    // CHECK: br label {{.*}}, !llvm.loop ![[LOOP_5:.*]]
+    List[i] = Value;
+  }
+}
+
 // CHECK: ![[LOOP_1]] = distinct !{![[LOOP_1]], [[MP:![0-9]+]], ![[PIPELINE_DISABLE:.*]]}
 // CHECK: ![[PIPELINE_DISABLE]] = !{!"llvm.loop.pipeline.disable", i1 true}
 
@@ -45,3 +54,6 @@ void pipeline_disabled_on_nested_loop(int *List, int Length, int Value) {
 // CHECK: ![[PIPELINE_II_10]] = !{!"llvm.loop.pipeline.initiationinterval", i32 10}
 
 // CHECK: ![[LOOP_4]] = distinct !{![[LOOP_4]], [[MP]], ![[PIPELINE_DISABLE]]}
+
+// CHECK: ![[LOOP_5]] = distinct !{![[LOOP_5]], [[MP]], ![[PIPELINE_ENABLE:.*]]}
+// CHECK: ![[PIPELINE_ENABLE]] = !{!"llvm.loop.pipeline.enable"}
diff --git a/clang/test/Parser/pragma-loop.cpp b/clang/test/Parser/pragma-loop.cpp
index 4078210f96e1b9..bb068b1d6c22b9 100644
--- a/clang/test/Parser/pragma-loop.cpp
+++ b/clang/test/Parser/pragma-loop.cpp
@@ -325,7 +325,7 @@ void foo(int *List, int Length) {
     List[i] = i;
   }
 
-#pragma clang loop pipeline(disable, extra)
+#pragma clang loop pipeline(enable, extra)
 /* expected-warning {{extra tokens at end of '#pragma clang loop pipeline' - ignored}}*/ while (i-6 < Length) {
     List[i] = i;
   }
diff --git a/clang/test/Parser/pragma-pipeline.cpp b/clang/test/Parser/pragma-pipeline.cpp
index e500d4d2d5fb25..7d7a8ebab12f07 100644
--- a/clang/test/Parser/pragma-pipeline.cpp
+++ b/clang/test/Parser/pragma-pipeline.cpp
@@ -6,6 +6,11 @@
 void test(int *List, int Length, int Value) {
   int i = 0;
 
+#pragma clang loop pipeline(enable)
+  for (int i = 0; i < Length; i++) {
+    List[i] = Value;
+  }
+
 #pragma clang loop pipeline(disable)
   for (int i = 0; i < Length; i++) {
     List[i] = Value;
@@ -17,8 +22,7 @@ void test(int *List, int Length, int Value) {
   }
 
 /* expected-error {{expected ')'}} */ #pragma clang loop pipeline(disable
-/* expected-error {{invalid argument; expected 'disable'}} */ #pragma clang loop pipeline(enable)
-/* expected-error {{invalid argument; expected 'disable'}} */ #pragma clang loop pipeline(error)
+/* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop pipeline(error)
 /* expected-error {{expected '('}} */ #pragma clang loop pipeline disable
 /* expected-error {{missing argument; expected an integer value}} */ #pragma clang loop pipeline_initiation_interval()
 /* expected-error {{use of undeclared identifier 'error'}} */ #pragma clang loop pipeline_initiation_interval(error)



More information about the cfe-commits mailing list