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

[flang] Implement !DIR$ VECTOR ALWAYS #93830

Merged
merged 6 commits into from
Jun 14, 2024
Merged

Conversation

DavidTruby
Copy link
Member

This patch implements support for the VECTOR ALWAYS directive, which forces
vectorization to occurr when possible regardless of a decision by the cost
model. This is done by adding an attribute to the branch into the loop in LLVM
to indicate that the loop should always be vectorized.

@llvmbot
Copy link
Collaborator

llvmbot commented May 30, 2024

@llvm/pr-subscribers-flang-fir-hlfir
@llvm/pr-subscribers-flang-parser

@llvm/pr-subscribers-flang-semantics

Author: David Truby (DavidTruby)

Changes

This patch implements support for the VECTOR ALWAYS directive, which forces
vectorization to occurr when possible regardless of a decision by the cost
model. This is done by adding an attribute to the branch into the loop in LLVM
to indicate that the loop should always be vectorized.


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

12 Files Affected:

  • (modified) flang/include/flang/Lower/PFTBuilder.h (+1)
  • (modified) flang/include/flang/Optimizer/Dialect/FIROps.h (+1)
  • (modified) flang/include/flang/Optimizer/Dialect/FIROps.td (+2-1)
  • (modified) flang/include/flang/Parser/dump-parse-tree.h (+1)
  • (modified) flang/include/flang/Parser/parse-tree.h (+2-1)
  • (modified) flang/lib/Lower/Bridge.cpp (+48-4)
  • (modified) flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp (+5-1)
  • (modified) flang/lib/Parser/Fortran-parsers.cpp (+3)
  • (modified) flang/lib/Parser/unparse.cpp (+3)
  • (modified) flang/lib/Semantics/resolve-names.cpp (+3)
  • (added) flang/test/Fir/vector-always.fir (+42)
  • (added) flang/test/Lower/vector-always.f90 (+29)
diff --git a/flang/include/flang/Lower/PFTBuilder.h b/flang/include/flang/Lower/PFTBuilder.h
index 9913f584133fa..aa83f1603c2a8 100644
--- a/flang/include/flang/Lower/PFTBuilder.h
+++ b/flang/include/flang/Lower/PFTBuilder.h
@@ -347,6 +347,7 @@ struct Evaluation : EvaluationVariant {
   parser::CharBlock position{};
   std::optional<parser::Label> label{};
   std::unique_ptr<EvaluationList> evaluationList; // nested evaluations
+  llvm::SmallVector<const parser::CompilerDirective *> dirs;
   Evaluation *parentConstruct{nullptr};  // set for nodes below the top level
   Evaluation *lexicalSuccessor{nullptr}; // set for leaf nodes, some directives
   Evaluation *controlSuccessor{nullptr}; // set for some leaf nodes
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.h b/flang/include/flang/Optimizer/Dialect/FIROps.h
index 9f07364ddb627..a21f8bbe17685 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.h
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.h
@@ -16,6 +16,7 @@
 #include "flang/Optimizer/Dialect/FortranVariableInterface.h"
 #include "mlir/Dialect/Arith/IR/Arith.h"
 #include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
 #include "mlir/Interfaces/LoopLikeInterface.h"
 #include "mlir/Interfaces/SideEffectInterfaces.h"
 
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index d9c1149040066..7bf68908e37dd 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -2096,7 +2096,8 @@ def fir_DoLoopOp : region_Op<"do_loop",
     Index:$step,
     Variadic<AnyType>:$initArgs,
     OptionalAttr<UnitAttr>:$unordered,
-    OptionalAttr<UnitAttr>:$finalValue
+    OptionalAttr<UnitAttr>:$finalValue,
+    OptionalAttr<LoopAnnotationAttr>:$loop_annotation
   );
   let results = (outs Variadic<AnyType>:$results);
   let regions = (region SizedRegion<1>:$region);
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 68ae50c312cde..8cf790650fb49 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -206,6 +206,7 @@ class ParseTreeDumper {
   NODE(CompilerDirective, IgnoreTKR)
   NODE(CompilerDirective, LoopCount)
   NODE(CompilerDirective, AssumeAligned)
+  NODE(CompilerDirective, VectorAlways)
   NODE(CompilerDirective, NameValue)
   NODE(CompilerDirective, Unrecognized)
   NODE(parser, ComplexLiteralConstant)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 0a40aa8b8f616..116e5f02ff32b 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -3316,6 +3316,7 @@ struct CompilerDirective {
     TUPLE_CLASS_BOILERPLATE(AssumeAligned);
     std::tuple<common::Indirection<Designator>, uint64_t> t;
   };
+  EMPTY_CLASS(VectorAlways);
   struct NameValue {
     TUPLE_CLASS_BOILERPLATE(NameValue);
     std::tuple<Name, std::optional<std::uint64_t>> t;
@@ -3323,7 +3324,7 @@ struct CompilerDirective {
   EMPTY_CLASS(Unrecognized);
   CharBlock source;
   std::variant<std::list<IgnoreTKR>, LoopCount, std::list<AssumeAligned>,
-      std::list<NameValue>, Unrecognized>
+      VectorAlways, std::list<NameValue>, Unrecognized>
       u;
 };
 
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 4e50de3e7ee9c..8ce68f9b8caab 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1881,7 +1881,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
 
     // Increment loop begin code. (Infinite/while code was already generated.)
     if (!infiniteLoop && !whileCondition)
-      genFIRIncrementLoopBegin(incrementLoopNestInfo);
+      genFIRIncrementLoopBegin(incrementLoopNestInfo, doStmtEval.dirs);
 
     // Loop body code.
     auto iter = eval.getNestedEvaluations().begin();
@@ -1926,8 +1926,22 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     return builder->createIntegerConstant(loc, controlType, 1); // step
   }
 
+  void addLoopAnnotationAttr(IncrementLoopInfo &info) {
+    mlir::BoolAttr f = mlir::BoolAttr::get(builder->getContext(), false);
+    mlir::LLVM::LoopVectorizeAttr va = mlir::LLVM::LoopVectorizeAttr::get(
+        builder->getContext(), f, {}, {}, {}, {}, {}, {});
+    mlir::LLVM::AccessGroupAttr ag =
+        mlir::LLVM::AccessGroupAttr::get(builder->getContext());
+    mlir::LLVM::LoopAnnotationAttr la = mlir::LLVM::LoopAnnotationAttr::get(
+        builder->getContext(), {}, va, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
+        {}, {}, {ag});
+    info.doLoop.setLoopAnnotationAttr(la);
+  }
+
   /// Generate FIR to begin a structured or unstructured increment loop nest.
-  void genFIRIncrementLoopBegin(IncrementLoopNestInfo &incrementLoopNestInfo) {
+  void genFIRIncrementLoopBegin(
+      IncrementLoopNestInfo &incrementLoopNestInfo,
+      llvm::SmallVectorImpl<const Fortran::parser::CompilerDirective *> &dirs) {
     assert(!incrementLoopNestInfo.empty() && "empty loop nest");
     mlir::Location loc = toLocation();
     for (IncrementLoopInfo &info : incrementLoopNestInfo) {
@@ -1978,6 +1992,15 @@ class FirConverter : public Fortran::lower::AbstractConverter {
         }
         if (info.hasLocalitySpecs())
           handleLocalitySpecs(info);
+
+        for (const auto *dir : dirs) {
+          std::visit(
+              Fortran::common::visitors{
+                  [&](const Fortran::parser::CompilerDirective::VectorAlways
+                          &d) { addLoopAnnotationAttr(info); },
+                  [&](const auto &) {}},
+              dir->u);
+        }
         continue;
       }
 
@@ -2508,8 +2531,29 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     }
   }
 
-  void genFIR(const Fortran::parser::CompilerDirective &) {
-    // TODO
+  void attachLoopDirective(const Fortran::parser::CompilerDirective &dir,
+                           Fortran::lower::pft::Evaluation *e) {
+    while (e->isDirective()) {
+      e = e->lexicalSuccessor;
+    }
+
+    if (e->isA<Fortran::parser::NonLabelDoStmt>()) {
+      e->dirs.push_back(&dir);
+    } else {
+      fir::emitFatalError(toLocation(), "loop directive must appear before a loop");
+    }
+  }
+
+  void genFIR(const Fortran::parser::CompilerDirective &dir) {
+    Fortran::lower::pft::Evaluation &eval = getEval();
+
+    std::visit(
+        Fortran::common::visitors{
+            [&](const Fortran::parser::CompilerDirective::VectorAlways &) {
+              attachLoopDirective(dir, &eval);
+            },
+            [&](const auto &) {}},
+        dir.u);
   }
 
   void genFIR(const Fortran::parser::OpenACCConstruct &acc) {
diff --git a/flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp b/flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp
index a233e7fbdcd1e..b40c06de8787b 100644
--- a/flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp
+++ b/flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp
@@ -132,10 +132,14 @@ class CfgLoopConv : public mlir::OpRewritePattern<fir::DoLoopOp> {
     auto comparison = rewriter.create<mlir::arith::CmpIOp>(
         loc, arith::CmpIPredicate::sgt, itersLeft, zero);
 
-    rewriter.create<mlir::cf::CondBranchOp>(
+    auto cond = rewriter.create<mlir::cf::CondBranchOp>(
         loc, comparison, firstBlock, llvm::ArrayRef<mlir::Value>(), endBlock,
         llvm::ArrayRef<mlir::Value>());
 
+    if (auto ann = loop.getLoopAnnotation()) {
+      cond->setAttr("loop_annotation", *ann);
+    }
+
     // The result of the loop operation is the values of the condition block
     // arguments except the induction variable on the last iteration.
     auto args = loop.getFinalValue()
diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index ff01974b549a1..d2241fb66a013 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -1276,10 +1276,13 @@ constexpr auto loopCount{
 constexpr auto assumeAligned{"ASSUME_ALIGNED" >>
     optionalList(construct<CompilerDirective::AssumeAligned>(
         indirect(designator), ":"_tok >> digitString64))};
+constexpr auto vectorAlways{
+    "VECTOR ALWAYS" >> construct<CompilerDirective::VectorAlways>()};
 TYPE_PARSER(beginDirective >> "DIR$ "_tok >>
     sourced((construct<CompilerDirective>(ignore_tkr) ||
                 construct<CompilerDirective>(loopCount) ||
                 construct<CompilerDirective>(assumeAligned) ||
+                construct<CompilerDirective>(vectorAlways) ||
                 construct<CompilerDirective>(
                     many(construct<CompilerDirective::NameValue>(
                         name, maybe(("="_tok || ":"_tok) >> digitString64))))) /
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index bdd968b19a43f..5a4c7550d64dd 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -1824,6 +1824,9 @@ class UnparseVisitor {
               Word("!DIR$ ASSUME_ALIGNED ");
               Walk(" ", assumeAligned, ", ");
             },
+            [&](const CompilerDirective::VectorAlways &valways) {
+              Word("!DIR$ VECTOR ALWAYS");
+            },
             [&](const std::list<CompilerDirective::NameValue> &names) {
               Walk("!DIR$ ", names, " ");
             },
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index a46c0f378d5d0..e65788a02b725 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -8854,6 +8854,9 @@ void ResolveNamesVisitor::Post(const parser::AssignedGotoStmt &x) {
 }
 
 void ResolveNamesVisitor::Post(const parser::CompilerDirective &x) {
+  if (const auto *dir{
+          std::get_if<parser::CompilerDirective::VectorAlways>(&x.u)})
+    return;
   if (const auto *tkr{
           std::get_if<std::list<parser::CompilerDirective::IgnoreTKR>>(&x.u)}) {
     if (currScope().IsTopLevel() ||
diff --git a/flang/test/Fir/vector-always.fir b/flang/test/Fir/vector-always.fir
new file mode 100644
index 0000000000000..b6dcf237ed59a
--- /dev/null
+++ b/flang/test/Fir/vector-always.fir
@@ -0,0 +1,42 @@
+// RUN: %flang_fc1 -emit-llvm -o - %s | FileCheck %s
+
+#access_group = #llvm.access_group<id = distinct[0]<>>
+#loop_vectorize = #llvm.loop_vectorize<disable = false>
+#loop_annotation = #llvm.loop_annotation<vectorize = #loop_vectorize, parallelAccesses = #access_group>
+
+// CHECK-LABEL: @vector_always_
+// CHECK:   br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[ANNOTATION:.*]]
+func.func @_QPvector_always() {
+    %c1 = arith.constant 1 : index
+    %c10_i32 = arith.constant 10 : i32
+    %c1_i32 = arith.constant 1 : i32
+    %c10 = arith.constant 10 : index
+    %0 = fir.alloca !fir.array<10xi32> {bindc_name = "a", uniq_name = "_QFvector_alwaysEa"}
+    %1 = fir.shape %c10 : (index) -> !fir.shape<1>
+    %2 = fir.declare %0(%1) {uniq_name = "_QFvector_alwaysEa"} : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>) -> !fir.ref<!fir.array<10xi32>>
+    %3 = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFvector_alwaysEi"}
+    %4 = fir.declare %3 {uniq_name = "_QFvector_alwaysEi"} : (!fir.ref<i32>) -> !fir.ref<i32>
+    %5 = fir.convert %c1_i32 : (i32) -> index
+    %6 = fir.convert %c10_i32 : (i32) -> index
+    %7 = fir.convert %5 : (index) -> i32
+    %8:2 = fir.do_loop %arg0 = %5 to %6 step %c1 iter_args(%arg1 = %7) -> (index, i32) attributes {loop_annotation = #loop_annotation} {
+      fir.store %arg1 to %4 : !fir.ref<i32>
+      %9 = fir.load %4 : !fir.ref<i32>
+      %10 = fir.load %4 : !fir.ref<i32>
+      %11 = fir.convert %10 : (i32) -> i64
+      %12 = fir.array_coor %2(%1) %11 : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
+      fir.store %9 to %12 : !fir.ref<i32>
+      %13 = arith.addi %arg0, %c1 : index
+      %14 = fir.convert %c1 : (index) -> i32
+      %15 = fir.load %4 : !fir.ref<i32>
+      %16 = arith.addi %15, %14 : i32
+      fir.result %13, %16 : index, i32
+    }
+    fir.store %8#1 to %4 : !fir.ref<i32>
+    return
+  }
+
+// CHECK: ![[ANNOTATION]] = distinct !{![[ANNOTATION]], ![[VECTORIZE:.*]], ![[PAR_ACCESS:.*]]}
+// CHECK: ![[VECTORIZE]] = !{!"llvm.loop.vectorize.enable", i1 true}
+// CHECK: ![[PAR_ACCESS]] = !{!"llvm.loop.parallel_accesses", ![[DISTINCT:.*]]}
+// CHECK: ![[DISTINCT]] = distinct !{}
diff --git a/flang/test/Lower/vector-always.f90 b/flang/test/Lower/vector-always.f90
new file mode 100644
index 0000000000000..1994163626f16
--- /dev/null
+++ b/flang/test/Lower/vector-always.f90
@@ -0,0 +1,29 @@
+! RUN: %flang_fc1 -emit-fir -o - %s | FileCheck %s
+
+! CHECK: #access_group = #llvm.access_group<id = distinct[0]<>>
+! CHECK: #access_group1 = #llvm.access_group<id = distinct[1]<>>
+! CHECK: #loop_vectorize = #llvm.loop_vectorize<disable = false>
+! CHECK: #loop_annotation = #llvm.loop_annotation<vectorize = #loop_vectorize, parallelAccesses = #access_group>
+! CHECK: #loop_annotation1 = #llvm.loop_annotation<vectorize = #loop_vectorize, parallelAccesses = #access_group1>
+
+! CHECK-LABEL: vector_always
+subroutine vector_always
+  integer :: a(10)
+  !dir$ vector always
+  !CHECK: fir.do_loop {{.*}} attributes {loop_annotation = #loop_annotation}
+  do i=1,10
+     a(i)=i
+  end do
+end subroutine vector_always
+
+
+! CHECK-LABEL: intermediate_directive
+subroutine intermediate_directive
+  integer :: a(10)
+  !dir$ vector always
+  !dir$ unknown
+  !CHECK: fir.do_loop {{.*}} attributes {loop_annotation = #loop_annotation1}
+  do i=1,10
+     a(i)=i
+  end do
+end subroutine intermediate_directive

@DavidTruby
Copy link
Member Author

Note: for now this only works on plain do loops, I'd like to extend it for unstructured loops/loops with labels etc but wanted feedback on the approach first

I'd appreciate feedback on the approach in general for tying directives to the following operations as this will be reused for other directives in future.

Copy link

github-actions bot commented May 30, 2024

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

@DavidTruby
Copy link
Member Author

@klausler Does the general approach look ok to you? I'm intending to do something similar for other directives that we need, as well as extending this to non-"NoLabelDoLoop"s

Copy link
Contributor

@kiranchandramohan kiranchandramohan left a comment

Choose a reason for hiding this comment

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

This is done by adding an attribute to the branch into the loop in LLVM
to indicate that the loop should always be vectorized.

Specify on which branch the attribute is being added here.

Also add to the summary all the variants not handled here.
-> unstructured loops
-> do concurrent
-> Array expressions (?)

Add an entry to the file https://github.com/llvm/llvm-project/blob/main/flang/docs/Directives.md

flang/test/Fir/vector-always.fir Outdated Show resolved Hide resolved
flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp Outdated Show resolved Hide resolved
flang/test/Lower/vector-always.f90 Outdated Show resolved Hide resolved
flang/include/flang/Lower/PFTBuilder.h Outdated Show resolved Hide resolved
flang/include/flang/Optimizer/Dialect/FIROps.td Outdated Show resolved Hide resolved
flang/include/flang/Parser/dump-parse-tree.h Outdated Show resolved Hide resolved
flang/lib/Lower/Bridge.cpp Outdated Show resolved Hide resolved
flang/lib/Parser/unparse.cpp Show resolved Hide resolved
flang/include/flang/Parser/parse-tree.h Show resolved Hide resolved
flang/lib/Lower/Bridge.cpp Outdated Show resolved Hide resolved
@klausler
Copy link
Contributor

@klausler Does the general approach look ok to you? I'm intending to do something similar for other directives that we need, as well as extending this to non-"NoLabelDoLoop"s

Yes. You might want to emit warnings about directives that don't immediately precede a DO construct to which they are relevant.

Copy link
Contributor

@tblah tblah left a comment

Choose a reason for hiding this comment

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

Thanks for your work on this. The general approach seems reasonable to me.

void addLoopAnnotationAttr(IncrementLoopInfo &info) {
mlir::BoolAttr f = mlir::BoolAttr::get(builder->getContext(), false);
mlir::LLVM::LoopVectorizeAttr va = mlir::LLVM::LoopVectorizeAttr::get(
builder->getContext(), f, {}, {}, {}, {}, {}, {});
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Please could you label what the false bool attribute is doing? Something like /*parameter_name=*/ will probably be enough.

Copy link
Contributor

Choose a reason for hiding this comment

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

@DavidTruby Is this comment addressed?

flang/lib/Lower/Bridge.cpp Show resolved Hide resolved
@tblah tblah requested a review from pawosm-arm May 31, 2024 11:57
Copy link
Contributor

@tblah tblah left a comment

Choose a reason for hiding this comment

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

Thanks for the update. This LGTM but don't merge until @klausler is happy

@klausler
Copy link
Contributor

klausler commented Jun 7, 2024

Thanks for the update. This LGTM but don't merge until @klausler is happy

I'm as happy as I ever get.

Copy link
Contributor

@kiranchandramohan kiranchandramohan left a comment

Choose a reason for hiding this comment

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

A few minor comments.

fir.result %c1, %c1_i32 : index, i32
}
return %8#1 : i32
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit:

Copy link
Member Author

Choose a reason for hiding this comment

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

Hi Kiran, what is the nit here?

Copy link
Contributor

Choose a reason for hiding this comment

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

No newline at end of file.

flang/lib/Semantics/canonicalize-directives.h Outdated Show resolved Hide resolved
flang/lib/Semantics/canonicalize-directives.h Outdated Show resolved Hide resolved
flang/lib/Semantics/canonicalize-directives.cpp Outdated Show resolved Hide resolved
flang/lib/Semantics/canonicalize-directives.cpp Outdated Show resolved Hide resolved
flang/lib/Semantics/canonicalize-directives.cpp Outdated Show resolved Hide resolved
flang/lib/Lower/Bridge.cpp Outdated Show resolved Hide resolved
@kiranchandramohan
Copy link
Contributor

Since there are some nontrivial changes here and it is the first time we are lowering directives, it is probably good to write a document that goes along with this patch. I am providing a sketch, you may choose to extend this or use a different one.

Introduction
Directives are commonly used in Fortran programs to specify additional actions to be performed by the compiler. The directives are always specified with the `!dir$` or `cdir$` prefix.

Loop Directives
Directives on loops are used to specify additional transformation to be performed by the compiler like enabling vectorisation, unrolling, interchange etc.

Vector Directive
This directive controls various aspects related to vectorisation. To begin with we will only support the most commonly used variant of the directive, vector always.

Vector Always Directive
This directive instructs the compiler to perform vectorisation without cost-model considerations.

Implementation Details
Parsing
The parser is model trivially to accept the vector always directive. In future this will be extended to handle other variants of the vector directive.

Semantics
Gives an error if the associated loop cannot be found for a loop directive.
Correct the parse tree if an executable directive falls in the specification section. Such directives are moved to the executable section.

Lowering
Evaluation is extended with a new field called dirs for representing Compiler directives associated with the Evaluation. While lowering the `CompilerDirective` the associated Do Loop's evaluation is found and the directive is added to it. This information is used during the lowering of the loop.

Representation in LLVM
Metadata (`llvm.loop.vectorize.enable`) is used in LLVM to inform the optimizer that a loop can be vectorized without considering its cost-model. 

Representation in MLIR 
LLVM dialect models this by an attribute called LoopAnnotation Attribute. The attribute can be added to the latch of the loop in LLVM and cf dialects. 

Representation in HLFIR/FIR
Unstructured Loops
Similar to MLIR representation. The LoopAnnotation Attribute will be added during lowering.

fir.do_loop
The fir.do_loop will be extended with a LoopAnnotation Attribute. This attribute will be added to the latch during the ControlFlowConverter Transformation/Conversion pass.

Array Expressions
TBD

Testing
The patch will be tested with an integration lit test that tests the whole feature (from source to LLVM IR).
Lit tests will be separately written for parsing, semantics and lowering stages.

This patch implements support for the VECTOR ALWAYS directive, which forces
vectorization to occurr when possible regardless of a decision by the cost
model. This is done by adding an attribute to the branch into the loop in LLVM
to indicate that the loop should always be vectorized.
Copy link
Contributor

@kiranchandramohan kiranchandramohan left a comment

Choose a reason for hiding this comment

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

Could you comment on all the comments that are not yet resolved?

with array expressions.

## Semantics
Dirctives that are associated with constructs must appear in the same section as
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Dirctives that are associated with constructs must appear in the same section as
Directives that are associated with constructs must appear in the same section as

Comment on lines 1 to 2
//===-- lib/Semantics/canonicalize-directives.h ------------------*- C++
//-*-===//
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: All these should be one line.

flang/docs/Directives.md Show resolved Hide resolved
flang/lib/Lower/Bridge.cpp Outdated Show resolved Hide resolved
@DavidTruby
Copy link
Member Author

Could you comment on all the comments that are not yet resolved?

I think I have resolved all of the comments now, are there any you think I have missed?

Copy link
Contributor

@kiranchandramohan kiranchandramohan left a comment

Choose a reason for hiding this comment

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

I have resolved a few since you have addressed them.

Please comment on the conversations I have specifically asked.

flang/lib/Lower/Bridge.cpp Show resolved Hide resolved
flang/lib/Semantics/resolve-names.cpp Outdated Show resolved Hide resolved
Copy link
Contributor

@kiranchandramohan kiranchandramohan left a comment

Choose a reason for hiding this comment

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

LGTM. Please wait a day before submitting incase other reviewers have comments.

@DavidTruby DavidTruby merged commit c6b6e18 into llvm:main Jun 14, 2024
8 checks passed
@psteinfeld
Copy link
Contributor

@DavidTruby, I'm seeing problems with this change. See issue #95780.

@DavidTruby DavidTruby deleted the vector_always branch June 18, 2024 11:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:fir-hlfir flang:parser flang:semantics flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants