[Flang] Add SIMD Compiler Directive (#192969)

Similar to #192674, this adds support for the SIMD Compiler Directive,
previously supported by legacy Fortran frontends such as classic-flang.
This operates in the same way as `VECTORALWAYS`, marking a DO loop to be
vectorised by the LLVM Backend.

The missing support was highlighted while building an opensource
benchmark, as build warnings were indicating that this compiler
directive was being ignored.
This commit is contained in:
Jack Styles 2026-04-22 14:54:37 +01:00 committed by GitHub
parent a623bd9135
commit f5e80c9858
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 123 additions and 3 deletions

View file

@ -67,6 +67,9 @@ A list of non-standard directives supported by Flang
* `!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].
* `!dir$ simd` works the same as `vector always` above, but provides an alternative
spelling and support for projects which would have used the classic-flang frontend
previously.
* `!dir$ vector vectorlength({fixed|scalable|<num>|<num>,fixed|<num>,scalable})`
specifies a hint to the compiler about the desired vectorization factor. If
`fixed` is used, the compiler should prefer fixed-width vectorization.

View file

@ -239,6 +239,7 @@ public:
NODE(CompilerDirective, NoUnroll)
NODE(CompilerDirective, NoUnrollAndJam)
NODE(CompilerDirective, Prefetch)
NODE(CompilerDirective, Simd)
NODE(parser, ComplexLiteralConstant)
NODE(parser, ComplexPart)
NODE(parser, ComponentArraySpec)

View file

@ -3345,6 +3345,7 @@ struct StmtFunctionStmt {
// !DIR$ INLINE
// !DIR$ NOINLINE
// !DIR$ IVDEP
// !DIR$ SIMD
// !DIR$ <anything else>
struct CompilerDirective {
UNION_CLASS_BOILERPLATE(CompilerDirective);
@ -3387,12 +3388,13 @@ struct CompilerDirective {
EMPTY_CLASS(Inline);
EMPTY_CLASS(NoInline);
EMPTY_CLASS(IVDep);
EMPTY_CLASS(Simd);
EMPTY_CLASS(Unrecognized);
CharBlock source;
std::variant<std::list<IgnoreTKR>, LoopCount, std::list<AssumeAligned>,
VectorAlways, VectorLength, std::list<NameValue>, Unroll, UnrollAndJam,
Unrecognized, NoVector, NoUnroll, NoUnrollAndJam, ForceInline, Inline,
NoInline, Prefetch, IVDep>
NoInline, Prefetch, IVDep, Simd>
u;
};

View file

@ -2869,6 +2869,11 @@ private:
mlir::LLVM::AccessGroupAttr::get(builder->getContext()));
has_attrs = true;
},
[&](const Fortran::parser::CompilerDirective::Simd &simd) {
disableVecAttr =
mlir::BoolAttr::get(builder->getContext(), false);
has_attrs = true;
},
[&](const auto &) {}},
dir->u);
}
@ -3630,6 +3635,9 @@ private:
[&](const Fortran::parser::CompilerDirective::IVDep &) {
attachDirectiveToLoop(dir, &eval);
},
[&](const Fortran::parser::CompilerDirective::Simd &) {
attachDirectiveToLoop(dir, &eval);
},
[&](const auto &) {}},
dir.u);
}

View file

@ -1341,6 +1341,7 @@ constexpr auto noinlineDir{
"NOINLINE" >> construct<CompilerDirective::NoInline>()};
constexpr auto inlineDir{"INLINE" >> construct<CompilerDirective::Inline>()};
constexpr auto ivdep{"IVDEP" >> construct<CompilerDirective::IVDep>()};
constexpr auto simd{"SIMD" >> construct<CompilerDirective::Simd>()};
TYPE_PARSER(beginDirective >> some(letter) >> "$ "_tok >>
sourced((construct<CompilerDirective>(ignore_tkr) ||
construct<CompilerDirective>(loopCount) ||
@ -1356,6 +1357,7 @@ TYPE_PARSER(beginDirective >> some(letter) >> "$ "_tok >>
construct<CompilerDirective>(noinlineDir) ||
construct<CompilerDirective>(forceinlineDir) ||
construct<CompilerDirective>(inlineDir) ||
construct<CompilerDirective>(simd) ||
construct<CompilerDirective>(ivdep) ||
construct<CompilerDirective>(
many(construct<CompilerDirective::NameValue>(

View file

@ -1886,6 +1886,7 @@ public:
Word("!DIR$ NOINLINE");
},
[&](const CompilerDirective::IVDep &) { Word("!DIR$ IVDEP"); },
[&](const CompilerDirective::Simd &) { Word("!DIR$ SIMD"); },
[&](const CompilerDirective::Unrecognized &) {
Word("!DIR$ ");
Word(x.source.ToString());

View file

@ -66,7 +66,8 @@ static bool IsExecutionDirective(const parser::CompilerDirective &dir) {
std::holds_alternative<parser::CompilerDirective::ForceInline>(dir.u) ||
std::holds_alternative<parser::CompilerDirective::Inline>(dir.u) ||
std::holds_alternative<parser::CompilerDirective::NoInline>(dir.u) ||
std::holds_alternative<parser::CompilerDirective::IVDep>(dir.u);
std::holds_alternative<parser::CompilerDirective::IVDep>(dir.u) ||
std::holds_alternative<parser::CompilerDirective::Simd>(dir.u);
}
void CanonicalizationOfDirectives::Post(parser::SpecificationPart &spec) {
@ -143,6 +144,9 @@ void CanonicalizationOfDirectives::Post(parser::Block &block) {
[&](parser::CompilerDirective::IVDep &) {
CheckLoopDirective(*dir, block, it);
},
[&](parser::CompilerDirective::Simd &) {
CheckLoopDirective(*dir, block, it);
},
[&](auto &) {}},
dir->u);
}

View file

@ -10402,7 +10402,8 @@ void ResolveNamesVisitor::Post(const parser::CompilerDirective &x) {
std::holds_alternative<parser::CompilerDirective::Inline>(x.u) ||
std::holds_alternative<parser::CompilerDirective::Prefetch>(x.u) ||
std::holds_alternative<parser::CompilerDirective::NoInline>(x.u) ||
std::holds_alternative<parser::CompilerDirective::IVDep>(x.u)) {
std::holds_alternative<parser::CompilerDirective::IVDep>(x.u) ||
std::holds_alternative<parser::CompilerDirective::Simd>(x.u)) {
return;
}
if (const auto *tkr{

View file

@ -0,0 +1,17 @@
! Test lowering of SIMD directive to ensure that correct cectorisation flags are
! applied to the do loops
! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
subroutine test1()
integer :: i
!DIR$ SIMD
do i = 1, 10
end do
end subroutine
! CHECK: #[[loop_vec:.*]] = #llvm.loop_vectorize<disable = false>
! CHECK: #[[loop_annotate:.*]] = #llvm.loop_annotation<vectorize = #[[loop_vec]]>
! CHECK :%[[.*]] = fir.do_loop %[[.*]] = %[[.*]] to %[[.*]] step %[[.*]] iter_args(%arg1 = %[[.*]]) -> (i32) attributes {loopAnnotation = #[[loop_annontate]]} {

View file

@ -0,0 +1,72 @@
! Test parsing and unparsing of SIMD Directive
! RUN: %flang_fc1 -fdebug-dump-parse-tree %s -o - | FileCheck %s --check-prefix="PARSE-TREE"
! RUN: %flang_fc1 -fdebug-unparse %s -o - | FileCheck %s --check-prefix="UNPARSE"
subroutine test()
integer :: i, n, sum
n = 10
sum = 0
!DIR$ SIMD
do i = 1, n
sum = sum + i
end do
end subroutine
! UNPARSE: SUBROUTINE test
! UNPARSE: INTEGER i, n, sum
! UNPARSE: n=10_4
! UNPARSE: sum=0_4
! UNPARSE: !DIR$ SIMD
! UNPARSE: DO i=1_4,n
! UNPARSE: sum=sum+i
! UNPARSE: END DO
! UNPARSE: END SUBROUTINE
! PARSE-TREE: ======================== Flang: parse tree dump ========================
! PARSE-TREE: Program -> ProgramUnit -> SubroutineSubprogram
! PARSE-TREE: | SubroutineStmt
! PARSE-TREE: | | Name = 'test'
! PARSE-TREE: | SpecificationPart
! PARSE-TREE: | | ImplicitPart ->
! PARSE-TREE: | | DeclarationConstruct -> SpecificationConstruct -> TypeDeclarationStmt
! PARSE-TREE: | | | DeclarationTypeSpec -> IntrinsicTypeSpec -> IntegerTypeSpec ->
! PARSE-TREE: | | | EntityDecl
! PARSE-TREE: | | | | Name = 'i'
! PARSE-TREE: | | | EntityDecl
! PARSE-TREE: | | | | Name = 'n'
! PARSE-TREE: | | | EntityDecl
! PARSE-TREE: | | | | Name = 'sum'
! PARSE-TREE: | ExecutionPart -> Block
! PARSE-TREE: | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt = 'n=10_4'
! PARSE-TREE: | | | Variable = 'n'
! PARSE-TREE: | | | | Designator -> DataRef -> Name = 'n'
! PARSE-TREE: | | | Expr = '10_4'
! PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '10'
! PARSE-TREE: | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt = 'sum=0_4'
! PARSE-TREE: | | | Variable = 'sum'
! PARSE-TREE: | | | | Designator -> DataRef -> Name = 'sum'
! PARSE-TREE: | | | Expr = '0_4'
! PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '0'
! PARSE-TREE: | | ExecutionPartConstruct -> ExecutableConstruct -> CompilerDirective -> Simd
! PARSE-TREE: | | ExecutionPartConstruct -> ExecutableConstruct -> DoConstruct
! PARSE-TREE: | | | NonLabelDoStmt
! PARSE-TREE: | | | | LoopControl -> LoopBounds
! PARSE-TREE: | | | | | Scalar -> Name = 'i'
! PARSE-TREE: | | | | | Scalar -> Expr = '1_4'
! PARSE-TREE: | | | | | | LiteralConstant -> IntLiteralConstant = '1'
! PARSE-TREE: | | | | | Scalar -> Expr = 'n'
! PARSE-TREE: | | | | | | Designator -> DataRef -> Name = 'n'
! PARSE-TREE: | | | Block
! PARSE-TREE: | | | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt = 'sum=sum+i'
! PARSE-TREE: | | | | | Variable = 'sum'
! PARSE-TREE: | | | | | | Designator -> DataRef -> Name = 'sum'
! PARSE-TREE: | | | | | Expr = 'sum+i'
! PARSE-TREE: | | | | | | Add
! PARSE-TREE: | | | | | | | Expr = 'sum'
! PARSE-TREE: | | | | | | | | Designator -> DataRef -> Name = 'sum'
! PARSE-TREE: | | | | | | | Expr = 'i'
! PARSE-TREE: | | | | | | | | Designator -> DataRef -> Name = 'i'
! PARSE-TREE: | | | EndDoStmt ->
! PARSE-TREE: | EndSubroutineStmt ->

View file

@ -0,0 +1,9 @@
! Check that appropriate errors are given when the SIMD Directive is used
! with no DO loop following
! RUN: %python %S/test_errors.py %s %flang -Werror
subroutine test()
!WARNING: A DO loop must follow the SIMD directive
!DIR$ SIMD
end subroutine