aboutsummaryrefslogtreecommitdiff
path: root/flang
diff options
context:
space:
mode:
authorDavid Truby <david.truby@arm.com>2024-06-14 14:10:41 +0100
committerGitHub <noreply@github.com>2024-06-14 14:10:41 +0100
commitc6b6e18c4d25305ab98b6eab752de99ea4e15344 (patch)
tree6220880a51b9fa9d4d54ec9413d4518415864186 /flang
parent8ab3f8ae0d39048e4bc1198514049813c6765fb6 (diff)
downloadllvm-c6b6e18c4d25305ab98b6eab752de99ea4e15344.zip
llvm-c6b6e18c4d25305ab98b6eab752de99ea4e15344.tar.gz
llvm-c6b6e18c4d25305ab98b6eab752de99ea4e15344.tar.bz2
[flang] Implement !DIR$ VECTOR ALWAYS (#93830)
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. This patch only implements this directive on plan structured do loops without labels. Support for unstructured loops and array expressions is planned for future patches.
Diffstat (limited to 'flang')
-rw-r--r--flang/docs/Directives.md59
-rw-r--r--flang/include/flang/Lower/PFTBuilder.h2
-rw-r--r--flang/include/flang/Optimizer/Dialect/FIROps.h1
-rw-r--r--flang/include/flang/Optimizer/Dialect/FIROps.td3
-rw-r--r--flang/include/flang/Parser/dump-parse-tree.h3
-rw-r--r--flang/include/flang/Parser/parse-tree.h3
-rw-r--r--flang/lib/Lower/Bridge.cpp49
-rw-r--r--flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp6
-rw-r--r--flang/lib/Parser/Fortran-parsers.cpp3
-rw-r--r--flang/lib/Parser/unparse.cpp3
-rw-r--r--flang/lib/Semantics/CMakeLists.txt1
-rw-r--r--flang/lib/Semantics/canonicalize-directives.cpp124
-rw-r--r--flang/lib/Semantics/canonicalize-directives.h22
-rw-r--r--flang/lib/Semantics/resolve-names.cpp3
-rw-r--r--flang/lib/Semantics/semantics.cpp2
-rw-r--r--flang/test/Fir/vector-always-cfg.fir32
-rw-r--r--flang/test/Fir/vector-always.fir21
-rw-r--r--flang/test/Integration/vector-always.f9014
-rw-r--r--flang/test/Lower/vector-always.f9026
-rw-r--r--flang/test/Parser/compiler-directives.f909
-rw-r--r--flang/test/Semantics/loop-directives.f9019
21 files changed, 396 insertions, 9 deletions
diff --git a/flang/docs/Directives.md b/flang/docs/Directives.md
index fe08b4f..f356f76 100644
--- a/flang/docs/Directives.md
+++ b/flang/docs/Directives.md
@@ -36,3 +36,62 @@ A list of non-standard directives supported by Flang
and is limited to 256.
[This directive is currently recognised by the parser, but not
handled by the other parts of the compiler].
+* `!dir$ vector always` forces vectorization on the following loop regardless
+ of cost model decisions. The loop must still be vectorizable.
+ [This directive currently only works on plain do loops without labels].
+
+# Directive Details
+
+## 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
+Some directives are associated with the following construct, for example loop
+directives. Directives on loops are used to specify additional transformation to
+be performed by the compiler like enabling vectorisation, unrolling, interchange
+etc.
+
+Currently loop directives are not accepted in the presence of OpenMP or OpenACC
+constructs on the loop. This should be implemented as it is used in some
+applications.
+
+### Array Expressions
+It is to be decided whether loop directives should also be able to be associated
+with array expressions.
+
+## Semantics
+Directives that are associated with constructs must appear in the same section
+as the construct they are associated with, for example loop directives must
+appear in the executable section as the loops appear there. To facilitate this
+the parse tree is corrected to move such directives that appear in the
+specification part into the execution part.
+
+When a directive that must be associated with a construct appears, a search
+forward from that directive to the next non-directive construct is performed to
+check that that construct matches the expected construct for the directive.
+Skipping other intermediate directives allows multiple directives to appear on
+the same construct.
+
+## Lowering
+Evaluation is extended with a new field called dirs for representing directives
+associated with that Evaluation. When lowering loop directives, the associated
+Do Loop's evaluation is found and the directive is added to it. This information
+is used only during the lowering of the loop.
+
+### Representation in LLVM
+The `llvm.loop` metadata is used in LLVM to provide information to the optimizer
+about the loop. For example, the `llvm.loop.vectorize.enable` metadata informs
+the optimizer that a loop can be vectorized without considering its cost-model.
+This attribute is added to the loop condition branch.
+
+### Representation in MLIR
+The MLIR LLVM dialect models this by an attribute called LoopAnnotation
+Attribute. The attribute can be added to the latch of the loop in the cf
+dialect and is then carried through lowering to the LLVM dialect.
+
+## Testing
+Since directives must maintain a flow from source to LLVM IR, an integration
+test is provided that tests the `vector always` directive, as well as individual
+lit tests for each of the parsing, semantics and lowering stages.
diff --git a/flang/include/flang/Lower/PFTBuilder.h b/flang/include/flang/Lower/PFTBuilder.h
index 83200eb..c2b600c 100644
--- a/flang/include/flang/Lower/PFTBuilder.h
+++ b/flang/include/flang/Lower/PFTBuilder.h
@@ -350,6 +350,8 @@ struct Evaluation : EvaluationVariant {
parser::CharBlock position{};
std::optional<parser::Label> label{};
std::unique_ptr<EvaluationList> evaluationList; // nested evaluations
+ // associated compiler directives
+ llvm::SmallVector<const parser::CompilerDirective *, 1> 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 9f07364..a21f8bb 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 e7da3af..baf0952 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -2160,7 +2160,8 @@ def fir_DoLoopOp : region_Op<"do_loop", [AttrSizedOperandSegments,
Variadic<AnyType>:$initArgs,
OptionalAttr<UnitAttr>:$unordered,
OptionalAttr<UnitAttr>:$finalValue,
- OptionalAttr<ArrayAttr>:$reduceAttrs
+ OptionalAttr<ArrayAttr>:$reduceAttrs,
+ OptionalAttr<LoopAnnotationAttr>:$loopAnnotation
);
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 4232e85..37c3370 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -201,11 +201,12 @@ public:
NODE(parser, CommonStmt)
NODE(CommonStmt, Block)
NODE(parser, CompilerDirective)
+ NODE(CompilerDirective, AssumeAligned)
NODE(CompilerDirective, IgnoreTKR)
NODE(CompilerDirective, LoopCount)
- NODE(CompilerDirective, AssumeAligned)
NODE(CompilerDirective, NameValue)
NODE(CompilerDirective, Unrecognized)
+ NODE(CompilerDirective, VectorAlways)
NODE(parser, ComplexLiteralConstant)
NODE(parser, ComplexPart)
NODE(parser, ComponentArraySpec)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index f0b9b68..548fcc8 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -3337,6 +3337,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;
@@ -3344,7 +3345,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 4dd0b7e..c73d432 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1935,7 +1935,7 @@ private:
// 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();
@@ -1980,8 +1980,20 @@ private:
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(), /*disable=*/f, {}, {}, {}, {}, {}, {});
+ mlir::LLVM::LoopAnnotationAttr la = mlir::LLVM::LoopAnnotationAttr::get(
+ builder->getContext(), {}, /*vectorize=*/va, {}, {}, {}, {}, {}, {}, {},
+ {}, {}, {}, {}, {}, {});
+ 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) {
@@ -2046,6 +2058,15 @@ private:
}
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;
}
@@ -2579,8 +2600,28 @@ private:
}
}
- void genFIR(const Fortran::parser::CompilerDirective &) {
- // TODO
+ void attachDirectiveToLoop(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 &) {
+ attachDirectiveToLoop(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 a233e7f..1af5a68 100644
--- a/flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp
+++ b/flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp
@@ -132,10 +132,14 @@ public:
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>());
+ // Copy loop annotations from the do loop to the loop entry condition.
+ 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 13f15c8..746d04a 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -1277,10 +1277,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 13ca230..036f306 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -1834,6 +1834,9 @@ public:
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/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt
index 8092065..41406ec 100644
--- a/flang/lib/Semantics/CMakeLists.txt
+++ b/flang/lib/Semantics/CMakeLists.txt
@@ -2,6 +2,7 @@ add_flang_library(FortranSemantics
assignment.cpp
attr.cpp
canonicalize-acc.cpp
+ canonicalize-directives.cpp
canonicalize-do.cpp
canonicalize-omp.cpp
check-acc-structure.cpp
diff --git a/flang/lib/Semantics/canonicalize-directives.cpp b/flang/lib/Semantics/canonicalize-directives.cpp
new file mode 100644
index 0000000..4bf3675
--- /dev/null
+++ b/flang/lib/Semantics/canonicalize-directives.cpp
@@ -0,0 +1,124 @@
+//===-- lib/Semantics/canonicalize-directives.cpp -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "canonicalize-directives.h"
+#include "flang/Parser/parse-tree-visitor.h"
+
+namespace Fortran::semantics {
+
+using namespace parser::literals;
+
+// Check that directives are associated with the correct constructs.
+// Directives that need to be associated with other constructs in the execution
+// part are moved to the execution part so they can be checked there.
+class CanonicalizationOfDirectives {
+public:
+ CanonicalizationOfDirectives(parser::Messages &messages)
+ : messages_{messages} {}
+
+ template <typename T> bool Pre(T &) { return true; }
+ template <typename T> void Post(T &) {}
+
+ // Move directives that must appear in the Execution part out of the
+ // Specification part.
+ void Post(parser::SpecificationPart &spec);
+ bool Pre(parser::ExecutionPart &x);
+
+ // Ensure that directives associated with constructs appear accompanying the
+ // construct.
+ void Post(parser::Block &block);
+
+private:
+ // Ensure that loop directives appear immediately before a loop.
+ void CheckLoopDirective(parser::CompilerDirective &dir, parser::Block &block,
+ std::list<parser::ExecutionPartConstruct>::iterator it);
+
+ parser::Messages &messages_;
+
+ // Directives to be moved to the Execution part from the Specification part.
+ std::list<common::Indirection<parser::CompilerDirective>>
+ directivesToConvert_;
+};
+
+bool CanonicalizeDirectives(
+ parser::Messages &messages, parser::Program &program) {
+ CanonicalizationOfDirectives dirs{messages};
+ Walk(program, dirs);
+ return !messages.AnyFatalError();
+}
+
+static bool IsExecutionDirective(const parser::CompilerDirective &dir) {
+ return std::holds_alternative<parser::CompilerDirective::VectorAlways>(dir.u);
+}
+
+void CanonicalizationOfDirectives::Post(parser::SpecificationPart &spec) {
+ auto &list{
+ std::get<std::list<common::Indirection<parser::CompilerDirective>>>(
+ spec.t)};
+ for (auto it{list.begin()}; it != list.end();) {
+ if (IsExecutionDirective(it->value())) {
+ directivesToConvert_.emplace_back(std::move(*it));
+ it = list.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+bool CanonicalizationOfDirectives::Pre(parser::ExecutionPart &x) {
+ auto origFirst{x.v.begin()};
+ for (auto &dir : directivesToConvert_) {
+ x.v.insert(origFirst,
+ parser::ExecutionPartConstruct{
+ parser::ExecutableConstruct{std::move(dir)}});
+ }
+
+ directivesToConvert_.clear();
+ return true;
+}
+
+template <typename T> T *GetConstructIf(parser::ExecutionPartConstruct &x) {
+ if (auto *y{std::get_if<parser::ExecutableConstruct>(&x.u)}) {
+ if (auto *z{std::get_if<common::Indirection<T>>(&y->u)}) {
+ return &z->value();
+ }
+ }
+ return nullptr;
+}
+
+void CanonicalizationOfDirectives::CheckLoopDirective(
+ parser::CompilerDirective &dir, parser::Block &block,
+ std::list<parser::ExecutionPartConstruct>::iterator it) {
+
+ // Skip over this and other compiler directives
+ while (GetConstructIf<parser::CompilerDirective>(*it)) {
+ ++it;
+ }
+
+ if (it == block.end() || !GetConstructIf<parser::DoConstruct>(*it)) {
+ std::string s{parser::ToUpperCaseLetters(dir.source.ToString())};
+ s.pop_back(); // Remove trailing newline from source string
+ messages_.Say(
+ dir.source, "A DO loop must follow the %s directive"_err_en_US, s);
+ }
+}
+
+void CanonicalizationOfDirectives::Post(parser::Block &block) {
+ for (auto it{block.begin()}; it != block.end(); ++it) {
+ if (auto *dir{GetConstructIf<parser::CompilerDirective>(*it)}) {
+ std::visit(
+ common::visitors{[&](parser::CompilerDirective::VectorAlways &) {
+ CheckLoopDirective(*dir, block, it);
+ },
+ [&](auto &) {}},
+ dir->u);
+ }
+ }
+}
+
+} // namespace Fortran::semantics
diff --git a/flang/lib/Semantics/canonicalize-directives.h b/flang/lib/Semantics/canonicalize-directives.h
new file mode 100644
index 0000000..89f8a0e
--- /dev/null
+++ b/flang/lib/Semantics/canonicalize-directives.h
@@ -0,0 +1,22 @@
+//===-- lib/Semantics/canonicalize-directives.h -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_SEMANTICS_CANONICALIZE_DIRECTIVES_H_
+#define FORTRAN_SEMANTICS_CANONICALIZE_DIRECTIVES_H_
+
+namespace Fortran::parser {
+struct Program;
+class Messages;
+} // namespace Fortran::parser
+
+namespace Fortran::semantics {
+bool CanonicalizeDirectives(
+ parser::Messages &messages, parser::Program &program);
+}
+
+#endif // FORTRAN_SEMANTICS_CANONICALIZE_DIRECTIVES_H_
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 17ff125..d4fe668 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -8899,6 +8899,9 @@ void ResolveNamesVisitor::Post(const parser::AssignedGotoStmt &x) {
}
void ResolveNamesVisitor::Post(const parser::CompilerDirective &x) {
+ if (std::holds_alternative<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/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index d51cc62..1bb0679 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -9,6 +9,7 @@
#include "flang/Semantics/semantics.h"
#include "assignment.h"
#include "canonicalize-acc.h"
+#include "canonicalize-directives.h"
#include "canonicalize-do.h"
#include "canonicalize-omp.h"
#include "check-acc-structure.h"
@@ -599,6 +600,7 @@ bool Semantics::Perform() {
CanonicalizeAcc(context_.messages(), program_) &&
CanonicalizeOmp(context_.messages(), program_) &&
CanonicalizeCUDA(program_) &&
+ CanonicalizeDirectives(context_.messages(), program_) &&
PerformStatementSemantics(context_, program_) &&
ModFileWriter{context_}.WriteAll();
}
diff --git a/flang/test/Fir/vector-always-cfg.fir b/flang/test/Fir/vector-always-cfg.fir
new file mode 100644
index 0000000..45c2ea0
--- /dev/null
+++ b/flang/test/Fir/vector-always-cfg.fir
@@ -0,0 +1,32 @@
+// RUN: fir-opt --fir-to-llvm-ir %s | FileCheck %s
+
+#access_group = #llvm.access_group<id = distinct[0]<>>
+// CHECK: #[[ACCESS:.*]] = #llvm.access_group<id = distinct[0]<>>
+#loop_vectorize = #llvm.loop_vectorize<disable = false>
+// CHECK: #[[VECTORIZE:.*]] = #llvm.loop_vectorize<disable = false>
+#loop_annotation = #llvm.loop_annotation<vectorize = #loop_vectorize, parallelAccesses = #access_group>
+// CHECK: #[[ANNOTATION:.*]] = #llvm.loop_annotation<vectorize = #[[VECTORIZE]], parallelAccesses = #[[ACCESS]]>
+
+func.func @_QPvector_always() -> i32 {
+ %c1 = arith.constant 1 : index
+ %c10_i32 = arith.constant 10 : i32
+ %c1_i32 = arith.constant 1 : i32
+ %c10 = arith.constant 10 : index
+ %0 = arith.subi %c10, %c1 : index
+ %1 = arith.addi %0, %c1 : index
+ %2 = arith.divsi %1, %c1 : index
+ cf.br ^bb1(%c1, %c1_i32, %2 : index, i32, index)
+^bb1(%3: index, %4: i32, %5: index): // 2 preds: ^bb0, ^bb2
+ %c0 = arith.constant 0 : index
+ %6 = arith.cmpi sgt, %5, %c0 : index
+ cf.cond_br %6, ^bb2, ^bb3 {loop_annotation = #loop_annotation}
+// CHECK: llvm.cond_br %{{.*}}, ^{{.*}}, ^{{.*}} {loop_annotation = #[[ANNOTATION]]}
+^bb2: // pred: ^bb1
+ %7 = arith.addi %3, %c1 : index
+ %c1_0 = arith.constant 1 : index
+ %8 = arith.subi %5, %c1_0 : index
+ cf.br ^bb1(%7, %c1_i32, %8 : index, i32, index)
+^bb3: // pred: ^bb1
+ return %4 : i32
+}
+
diff --git a/flang/test/Fir/vector-always.fir b/flang/test/Fir/vector-always.fir
new file mode 100644
index 0000000..00eb0e7
--- /dev/null
+++ b/flang/test/Fir/vector-always.fir
@@ -0,0 +1,21 @@
+// RUN: fir-opt --cfg-conversion %s | FileCheck %s
+
+#access_group = #llvm.access_group<id = distinct[0]<>>
+// CHECK: #[[ACCESS:.*]] = #llvm.access_group<id = distinct[0]<>>
+#loop_vectorize = #llvm.loop_vectorize<disable = false>
+// CHECK: #[[VECTORIZE:.*]] = #llvm.loop_vectorize<disable = false>
+#loop_annotation = #llvm.loop_annotation<vectorize = #loop_vectorize, parallelAccesses = #access_group>
+// CHECK: #[[ANNOTATION:.*]] = #llvm.loop_annotation<vectorize = #[[VECTORIZE]], parallelAccesses = #[[ACCESS]]>
+
+// CHECK-LABEL: @_QPvector_always
+func.func @_QPvector_always() -> i32 {
+ %c1 = arith.constant 1 : index
+ %c10_i32 = arith.constant 10 : i32
+ %c1_i32 = arith.constant 1 : i32
+ %c10 = arith.constant 10 : index
+// CHECK: cf.cond_br %{{.*}}, ^{{.*}}, ^{{.*}} {loop_annotation = #[[ANNOTATION]]}
+ %8:2 = fir.do_loop %arg0 = %c1 to %c10 step %c1 iter_args(%arg1 = %c1_i32) -> (index, i32) attributes {loopAnnotation = #loop_annotation} {
+ fir.result %c1, %c1_i32 : index, i32
+ }
+ return %8#1 : i32
+ }
diff --git a/flang/test/Integration/vector-always.f90 b/flang/test/Integration/vector-always.f90
new file mode 100644
index 0000000..7216698
--- /dev/null
+++ b/flang/test/Integration/vector-always.f90
@@ -0,0 +1,14 @@
+! RUN: %flang_fc1 -emit-llvm -o - %s | FileCheck %s
+
+! CHECK-LABEL: vector_always
+subroutine vector_always
+ integer :: a(10)
+ !dir$ vector always
+ ! CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[ANNOTATION:.*]]
+ do i=1,10
+ a(i)=i
+ end do
+end subroutine vector_always
+
+! CHECK: ![[ANNOTATION]] = distinct !{![[ANNOTATION]], ![[VECTORIZE:.*]]}
+! CHECK: ![[VECTORIZE]] = !{!"llvm.loop.vectorize.enable", i1 true}
diff --git a/flang/test/Lower/vector-always.f90 b/flang/test/Lower/vector-always.f90
new file mode 100644
index 0000000..1822fc3
--- /dev/null
+++ b/flang/test/Lower/vector-always.f90
@@ -0,0 +1,26 @@
+! RUN: %flang_fc1 -emit-hlfir -o - %s | FileCheck %s
+
+! CHECK: #loop_vectorize = #llvm.loop_vectorize<disable = false>
+! CHECK: #loop_annotation = #llvm.loop_annotation<vectorize = #loop_vectorize>
+
+! CHECK-LABEL: vector_always
+subroutine vector_always
+ integer :: a(10)
+ !dir$ vector always
+ !CHECK: fir.do_loop {{.*}} attributes {loopAnnotation = #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 {loopAnnotation = #loop_annotation}
+ do i=1,10
+ a(i)=i
+ end do
+end subroutine intermediate_directive
diff --git a/flang/test/Parser/compiler-directives.f90 b/flang/test/Parser/compiler-directives.f90
index d4c99ae..246eaf9 100644
--- a/flang/test/Parser/compiler-directives.f90
+++ b/flang/test/Parser/compiler-directives.f90
@@ -1,4 +1,4 @@
-! RUN: %flang_fc1 -fdebug-unparse %s 2>&1
+! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
! Test that compiler directives can appear in various places.
@@ -28,3 +28,10 @@ module m
!dir$ align : 1024 :: d
end type stuff
end
+
+subroutine vector_always
+ !dir$ vector always
+ ! CHECK: !DIR$ VECTOR ALWAYS
+ do i=1,10
+ enddo
+end subroutine
diff --git a/flang/test/Semantics/loop-directives.f90 b/flang/test/Semantics/loop-directives.f90
new file mode 100644
index 0000000..e2807c1
--- /dev/null
+++ b/flang/test/Semantics/loop-directives.f90
@@ -0,0 +1,19 @@
+! RUN: %python %S/test_errors.py %s %flang
+
+subroutine empty
+ ! ERROR: A DO loop must follow the VECTOR ALWAYS directive
+ !dir$ vector always
+end subroutine empty
+
+subroutine non_do
+ ! ERROR: A DO loop must follow the VECTOR ALWAYS directive
+ !dir$ vector always
+ a = 1
+end subroutine non_do
+
+subroutine execution_part
+ do i=1,10
+ ! ERROR: A DO loop must follow the VECTOR ALWAYS directive
+ !dir$ vector always
+ end do
+end subroutine execution_part