Skip to content

Commit 5592699

Browse files
committed
Add check for directive location in semantics
Refactor tests
1 parent 1d33514 commit 5592699

File tree

13 files changed

+238
-42
lines changed

13 files changed

+238
-42
lines changed

flang/docs/Directives.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,6 @@ A list of non-standard directives supported by Flang
3636
and is limited to 256.
3737
[This directive is currently recognised by the parser, but not
3838
handled by the other parts of the compiler].
39+
* `!dir$ vector always` forces vectorization on the following loop regardless
40+
of cost model decisions. The loop must still be vectorizable.
41+
[This directive currently only works on plain do loops without labels].

flang/include/flang/Parser/dump-parse-tree.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,12 @@ class ParseTreeDumper {
201201
NODE(parser, CommonStmt)
202202
NODE(CommonStmt, Block)
203203
NODE(parser, CompilerDirective)
204+
NODE(CompilerDirective, AssumeAligned)
204205
NODE(CompilerDirective, IgnoreTKR)
205206
NODE(CompilerDirective, LoopCount)
206-
NODE(CompilerDirective, AssumeAligned)
207-
NODE(CompilerDirective, VectorAlways)
208207
NODE(CompilerDirective, NameValue)
209208
NODE(CompilerDirective, Unrecognized)
209+
NODE(CompilerDirective, VectorAlways)
210210
NODE(parser, ComplexLiteralConstant)
211211
NODE(parser, ComplexPart)
212212
NODE(parser, ComponentArraySpec)

flang/lib/Lower/Bridge.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1977,12 +1977,13 @@ class FirConverter : public Fortran::lower::AbstractConverter {
19771977
void addLoopAnnotationAttr(IncrementLoopInfo &info) {
19781978
mlir::BoolAttr f = mlir::BoolAttr::get(builder->getContext(), false);
19791979
mlir::LLVM::LoopVectorizeAttr va = mlir::LLVM::LoopVectorizeAttr::get(
1980-
builder->getContext(), f, {}, {}, {}, {}, {}, {});
1980+
builder->getContext(), /*disable=*/f, {}, {}, {}, {}, {}, {});
1981+
// Create distinct access group
19811982
mlir::LLVM::AccessGroupAttr ag =
19821983
mlir::LLVM::AccessGroupAttr::get(builder->getContext());
19831984
mlir::LLVM::LoopAnnotationAttr la = mlir::LLVM::LoopAnnotationAttr::get(
1984-
builder->getContext(), {}, va, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
1985-
{}, {}, {ag});
1985+
builder->getContext(), {}, /*vectorize=*/va, {}, {}, {}, {}, {}, {}, {},
1986+
{}, {}, {}, {}, {}, /*parallelAccess=*/{ag});
19861987
info.doLoop.setLoopAnnotationAttr(la);
19871988
}
19881989

flang/lib/Semantics/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ add_flang_library(FortranSemantics
22
assignment.cpp
33
attr.cpp
44
canonicalize-acc.cpp
5+
canonicalize-directives.cpp
56
canonicalize-do.cpp
67
canonicalize-omp.cpp
78
check-acc-structure.cpp
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//===-- lib/Semantics/check-directives.cpp --------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "canonicalize-directives.h"
10+
#include "flang/Parser/parse-tree-visitor.h"
11+
12+
namespace Fortran::semantics {
13+
14+
using namespace parser::literals;
15+
16+
// Check that directives are associated with the correct constructs
17+
class CanonicalizationOfDirectives {
18+
public:
19+
CanonicalizationOfDirectives(parser::Messages &messages)
20+
: messages_{messages} {}
21+
22+
template <typename T> bool Pre(T &) { return true; }
23+
template <typename T> void Post(T &) {}
24+
25+
// Move directives that must appear in the Execution part out of the
26+
// Specification part.
27+
void Post(parser::SpecificationPart &spec);
28+
bool Pre(parser::ExecutionPart &x);
29+
30+
// Ensure that directives associated with constructs appear accompanying the
31+
// construct.
32+
void Post(parser::Block &block);
33+
34+
private:
35+
// Ensure that loop directives appear immediately before a loop.
36+
void CheckLoopDirective(parser::CompilerDirective &dir, parser::Block &block,
37+
std::list<parser::ExecutionPartConstruct>::iterator it);
38+
39+
parser::Messages &messages_;
40+
41+
// Directives to be moved to the Execution part from the Specification part.
42+
std::list<common::Indirection<parser::CompilerDirective>>
43+
directivesToConvert_;
44+
};
45+
46+
bool CanonicalizeDirectives(
47+
parser::Messages &messages, parser::Program &program) {
48+
CanonicalizationOfDirectives dirs{messages};
49+
Walk(program, dirs);
50+
return !messages.AnyFatalError();
51+
}
52+
53+
static bool IsExecutionDirective(const parser::CompilerDirective &dir) {
54+
return std::holds_alternative<parser::CompilerDirective::VectorAlways>(dir.u);
55+
}
56+
57+
void CanonicalizationOfDirectives::Post(parser::SpecificationPart &spec) {
58+
auto &list{
59+
std::get<std::list<common::Indirection<parser::CompilerDirective>>>(
60+
spec.t)};
61+
for (auto it{list.begin()}; it != list.end();) {
62+
if (IsExecutionDirective(it->value())) {
63+
directivesToConvert_.emplace_back(std::move(*it));
64+
it = list.erase(it);
65+
} else {
66+
++it;
67+
}
68+
}
69+
}
70+
71+
bool CanonicalizationOfDirectives::Pre(parser::ExecutionPart &x) {
72+
auto origFirst{x.v.begin()};
73+
for (auto &dir : directivesToConvert_) {
74+
x.v.insert(origFirst,
75+
parser::ExecutionPartConstruct{
76+
parser::ExecutableConstruct{std::move(dir)}});
77+
}
78+
79+
directivesToConvert_.clear();
80+
return true;
81+
}
82+
83+
template <typename T> T *GetConstructIf(parser::ExecutionPartConstruct &x) {
84+
if (auto *y{std::get_if<parser::ExecutableConstruct>(&x.u)}) {
85+
if (auto *z{std::get_if<common::Indirection<T>>(&y->u)}) {
86+
return &z->value();
87+
}
88+
}
89+
return nullptr;
90+
}
91+
92+
void CanonicalizationOfDirectives::CheckLoopDirective(
93+
parser::CompilerDirective &dir, parser::Block &block,
94+
std::list<parser::ExecutionPartConstruct>::iterator it) {
95+
96+
// Skip over this and other compiler directives
97+
while (GetConstructIf<parser::CompilerDirective>(*it)) {
98+
++it;
99+
}
100+
101+
if (it == block.end() || !GetConstructIf<parser::DoConstruct>(*it)) {
102+
std::string s{parser::ToUpperCaseLetters(dir.source.ToString())};
103+
s.pop_back(); // Remove trailing newline from source string
104+
messages_.Say(
105+
dir.source, "A DO loop must follow the %s directive"_err_en_US, s);
106+
}
107+
}
108+
109+
void CanonicalizationOfDirectives::Post(parser::Block &block) {
110+
for (auto it = block.begin(); it != block.end(); ++it) {
111+
if (auto *dir{GetConstructIf<parser::CompilerDirective>(*it)}) {
112+
std::visit(
113+
common::visitors{[&](parser::CompilerDirective::VectorAlways &) {
114+
CheckLoopDirective(*dir, block, it);
115+
},
116+
[&](auto &) {}},
117+
dir->u);
118+
}
119+
}
120+
}
121+
122+
} // namespace Fortran::semantics
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//===-- lib/Semantics/check-directives.h ------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef FORTRAN_SEMANTICS_CHECK_DIRECTIVES_H_
10+
#define FORTRAN_SEMANTICS_CHECK_DIRECTIVES_H_
11+
12+
namespace Fortran::parser {
13+
struct Program;
14+
class Messages;
15+
} // namespace Fortran::parser
16+
17+
namespace Fortran::semantics {
18+
bool CanonicalizeDirectives(
19+
parser::Messages &messages, parser::Program &program);
20+
}
21+
22+
#endif // FORTRAN_SEMANTICS_CHECK_DIRECTIVES_H_

flang/lib/Semantics/resolve-names.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8886,10 +8886,8 @@ void ResolveNamesVisitor::Post(const parser::AssignedGotoStmt &x) {
88868886
}
88878887

88888888
void ResolveNamesVisitor::Post(const parser::CompilerDirective &x) {
8889-
//if (const auto *dir{
8890-
// std::get_if<parser::CompilerDirective::VectorAlways>(&x.u)})
8891-
8892-
if (std::holds_alternative<parser::CompilerDirective::VectorAlways>(x.u)) {
8889+
if (const auto *dir{
8890+
std::get_if<parser::CompilerDirective::VectorAlways>(&x.u)}) {
88938891
return;
88948892
}
88958893
if (const auto *tkr{

flang/lib/Semantics/semantics.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "flang/Semantics/semantics.h"
1010
#include "assignment.h"
1111
#include "canonicalize-acc.h"
12+
#include "canonicalize-directives.h"
1213
#include "canonicalize-do.h"
1314
#include "canonicalize-omp.h"
1415
#include "check-acc-structure.h"
@@ -599,6 +600,7 @@ bool Semantics::Perform() {
599600
CanonicalizeAcc(context_.messages(), program_) &&
600601
CanonicalizeOmp(context_.messages(), program_) &&
601602
CanonicalizeCUDA(program_) &&
603+
CanonicalizeDirectives(context_.messages(), program_) &&
602604
PerformStatementSemantics(context_, program_) &&
603605
ModFileWriter{context_}.WriteAll();
604606
}

flang/test/Fir/vector-always-cfg.fir

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: fir-opt --fir-to-llvm-ir %s | FileCheck %s
2+
3+
#access_group = #llvm.access_group<id = distinct[0]<>>
4+
// CHECK: #[[ACCESS:.*]] = #llvm.access_group<id = distinct[0]<>>
5+
#loop_vectorize = #llvm.loop_vectorize<disable = false>
6+
// CHECK: #[[VECTORIZE:.*]] = #llvm.loop_vectorize<disable = false>
7+
#loop_annotation = #llvm.loop_annotation<vectorize = #loop_vectorize, parallelAccesses = #access_group>
8+
// CHECK: #[[ANNOTATION:.*]] = #llvm.loop_annotation<vectorize = #[[VECTORIZE]], parallelAccesses = #[[ACCESS]]>
9+
10+
func.func @_QPvector_always() -> i32 {
11+
%c1 = arith.constant 1 : index
12+
%c10_i32 = arith.constant 10 : i32
13+
%c1_i32 = arith.constant 1 : i32
14+
%c10 = arith.constant 10 : index
15+
%0 = arith.subi %c10, %c1 : index
16+
%1 = arith.addi %0, %c1 : index
17+
%2 = arith.divsi %1, %c1 : index
18+
cf.br ^bb1(%c1, %c1_i32, %2 : index, i32, index)
19+
^bb1(%3: index, %4: i32, %5: index): // 2 preds: ^bb0, ^bb2
20+
%c0 = arith.constant 0 : index
21+
%6 = arith.cmpi sgt, %5, %c0 : index
22+
cf.cond_br %6, ^bb2, ^bb3 {loop_annotation = #loop_annotation}
23+
// CHECK: llvm.cond_br %{{.*}}, ^{{.*}}, ^{{.*}} {loop_annotation = #[[ANNOTATION]]}
24+
^bb2: // pred: ^bb1
25+
%7 = arith.addi %3, %c1 : index
26+
%c1_0 = arith.constant 1 : index
27+
%8 = arith.subi %5, %c1_0 : index
28+
cf.br ^bb1(%7, %c1_i32, %8 : index, i32, index)
29+
^bb3: // pred: ^bb1
30+
return %4 : i32
31+
}
32+

flang/test/Fir/vector-always.fir

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,21 @@
1-
// RUN: %flang_fc1 -emit-llvm -o - %s | FileCheck %s
1+
// RUN: fir-opt --cfg-conversion %s | FileCheck %s
22

33
#access_group = #llvm.access_group<id = distinct[0]<>>
4+
// CHECK: #[[ACCESS:.*]] = #llvm.access_group<id = distinct[0]<>>
45
#loop_vectorize = #llvm.loop_vectorize<disable = false>
6+
// CHECK: #[[VECTORIZE:.*]] = #llvm.loop_vectorize<disable = false>
57
#loop_annotation = #llvm.loop_annotation<vectorize = #loop_vectorize, parallelAccesses = #access_group>
8+
// CHECK: #[[ANNOTATION:.*]] = #llvm.loop_annotation<vectorize = #[[VECTORIZE]], parallelAccesses = #[[ACCESS]]>
69

7-
// CHECK-LABEL: @vector_always_
8-
// CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[ANNOTATION:.*]]
9-
func.func @_QPvector_always() {
10+
// CHECK-LABEL: @_QPvector_always
11+
func.func @_QPvector_always() -> i32 {
1012
%c1 = arith.constant 1 : index
1113
%c10_i32 = arith.constant 10 : i32
1214
%c1_i32 = arith.constant 1 : i32
1315
%c10 = arith.constant 10 : index
14-
%0 = fir.alloca !fir.array<10xi32> {bindc_name = "a", uniq_name = "_QFvector_alwaysEa"}
15-
%1 = fir.shape %c10 : (index) -> !fir.shape<1>
16-
%2 = fir.declare %0(%1) {uniq_name = "_QFvector_alwaysEa"} : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>) -> !fir.ref<!fir.array<10xi32>>
17-
%3 = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFvector_alwaysEi"}
18-
%4 = fir.declare %3 {uniq_name = "_QFvector_alwaysEi"} : (!fir.ref<i32>) -> !fir.ref<i32>
19-
%5 = fir.convert %c1_i32 : (i32) -> index
20-
%6 = fir.convert %c10_i32 : (i32) -> index
21-
%7 = fir.convert %5 : (index) -> i32
22-
%8:2 = fir.do_loop %arg0 = %5 to %6 step %c1 iter_args(%arg1 = %7) -> (index, i32) attributes {loopAnnotation = #loop_annotation} {
23-
fir.store %arg1 to %4 : !fir.ref<i32>
24-
%9 = fir.load %4 : !fir.ref<i32>
25-
%10 = fir.load %4 : !fir.ref<i32>
26-
%11 = fir.convert %10 : (i32) -> i64
27-
%12 = fir.array_coor %2(%1) %11 : (!fir.ref<!fir.array<10xi32>>, !fir.shape<1>, i64) -> !fir.ref<i32>
28-
fir.store %9 to %12 : !fir.ref<i32>
29-
%13 = arith.addi %arg0, %c1 : index
30-
%14 = fir.convert %c1 : (index) -> i32
31-
%15 = fir.load %4 : !fir.ref<i32>
32-
%16 = arith.addi %15, %14 : i32
33-
fir.result %13, %16 : index, i32
16+
// CHECK: cf.cond_br %{{.*}}, ^{{.*}}, ^{{.*}} {loop_annotation = #[[ANNOTATION]]}
17+
%8:2 = fir.do_loop %arg0 = %c1 to %c10 step %c1 iter_args(%arg1 = %c1_i32) -> (index, i32) attributes {loopAnnotation = #loop_annotation} {
18+
fir.result %c1, %c1_i32 : index, i32
3419
}
35-
fir.store %8#1 to %4 : !fir.ref<i32>
36-
return
37-
}
38-
39-
// CHECK: ![[ANNOTATION]] = distinct !{![[ANNOTATION]], ![[VECTORIZE:.*]], ![[PAR_ACCESS:.*]]}
40-
// CHECK: ![[VECTORIZE]] = !{!"llvm.loop.vectorize.enable", i1 true}
41-
// CHECK: ![[PAR_ACCESS]] = !{!"llvm.loop.parallel_accesses", ![[DISTINCT:.*]]}
42-
// CHECK: ![[DISTINCT]] = distinct !{}
20+
return %8#1 : i32
21+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
! RUN: %flang_fc1 -emit-llvm -o - %s | FileCheck %s
2+
3+
! CHECK-LABEL: vector_always
4+
subroutine vector_always
5+
integer :: a(10)
6+
!dir$ vector always
7+
! CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[ANNOTATION:.*]]
8+
do i=1,10
9+
a(i)=i
10+
end do
11+
end subroutine vector_always
12+
13+
! CHECK: ![[ANNOTATION]] = distinct !{![[ANNOTATION]], ![[VECTORIZE:.*]], ![[PAR_ACCESS:.*]]}
14+
! CHECK: ![[VECTORIZE]] = !{!"llvm.loop.vectorize.enable", i1 true}
15+
! CHECK: ![[PAR_ACCESS]] = !{!"llvm.loop.parallel_accesses", ![[DISTINCT:.*]]}
16+
! CHECK: ![[DISTINCT]] = distinct !{}

flang/test/Parser/compiler-directives.f90

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1
1+
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
22

33
! Test that compiler directives can appear in various places.
44

@@ -28,3 +28,10 @@ module m
2828
!dir$ align : 1024 :: d
2929
end type stuff
3030
end
31+
32+
subroutine vector_always
33+
!dir$ vector always
34+
! CHECK: !DIR$ VECTOR ALWAYS
35+
do i=1,10
36+
enddo
37+
end subroutine
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
! RUN: %python %S/test_errors.py %s %flang
2+
3+
program empty
4+
! ERROR: A DO loop must follow the VECTOR ALWAYS directive
5+
!dir$ vector always
6+
end program empty
7+
8+
program non_do
9+
! ERROR: A DO loop must follow the VECTOR ALWAYS directive
10+
!dir$ vector always
11+
a = 1
12+
end program non_do
13+

0 commit comments

Comments
 (0)