diff --git a/flang/examples/CMakeLists.txt b/flang/examples/CMakeLists.txt --- a/flang/examples/CMakeLists.txt +++ b/flang/examples/CMakeLists.txt @@ -12,3 +12,4 @@ ) add_subdirectory(PrintFlangFunctionNames) +add_subdirectory(flang-omp-report-plugin) diff --git a/flang/examples/flang-omp-report-plugin/CMakeLists.txt b/flang/examples/flang-omp-report-plugin/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/flang/examples/flang-omp-report-plugin/CMakeLists.txt @@ -0,0 +1,6 @@ +add_llvm_library( + flangOmpReport + MODULE + flang-omp-report.cpp + flang-omp-report-visitor.cpp +) diff --git a/flang/examples/flang-omp-report-plugin/flang-omp-report-visitor.h b/flang/examples/flang-omp-report-plugin/flang-omp-report-visitor.h new file mode 100644 --- /dev/null +++ b/flang/examples/flang-omp-report-plugin/flang-omp-report-visitor.h @@ -0,0 +1,106 @@ +//===-- examples/flang-omp-report-plugin/flang-omp-report-visitor.h -------===// +// +// 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_FLANG_OMP_REPORT_VISITOR_H +#define FORTRAN_FLANG_OMP_REPORT_VISITOR_H + +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Parser/parsing.h" + +#include +#include +#include +#include + +namespace Fortran { +namespace parser { +struct ClauseInfo { + std::string clause; + std::string clauseDetails; + ClauseInfo() {} + ClauseInfo(const std::string &c, const std::string &cd) + : clause{c}, clauseDetails{cd} {} + ClauseInfo(const std::pair &p) + : clause{std::get<0>(p)}, clauseDetails{std::get<1>(p)} {} +}; +bool operator<(const ClauseInfo &a, const ClauseInfo &b); +bool operator==(const ClauseInfo &a, const ClauseInfo &b); +bool operator!=(const ClauseInfo &a, const ClauseInfo &b); + +struct LogRecord { + std::string file; + int line; + std::string construct; + std::vector clauses; +}; +bool operator==(const LogRecord &a, const LogRecord &b); +bool operator!=(const LogRecord &a, const LogRecord &b); + +using OmpWrapperType = + std::variant; + +struct OpenMPCounterVisitor { + std::string normalize_construct_name(std::string s); + ClauseInfo normalize_clause_name(const std::string &s); + SourcePosition getLocation(const OmpWrapperType &w); + SourcePosition getLocation(const OpenMPDeclarativeConstruct &c); + SourcePosition getLocation(const OpenMPConstruct &c); + + std::string getName(const OmpWrapperType &w); + std::string getName(const OpenMPDeclarativeConstruct &c); + std::string getName(const OpenMPConstruct &c); + + template bool Pre(const A &) { return true; } + template void Post(const A &) {} + bool Pre(const OpenMPDeclarativeConstruct &c); + bool Pre(const OpenMPConstruct &c); + bool Pre(const OmpEndLoopDirective &c); + bool Pre(const DoConstruct &); + + void Post(const OpenMPDeclarativeConstruct &); + void Post(const OpenMPConstruct &); + void PostConstructsCommon(); + void Post(const OmpEndLoopDirective &c); + + void Post(const OmpProcBindClause::Type &c); + void Post(const OmpDefaultClause::Type &c); + void Post(const OmpDefaultmapClause::ImplicitBehavior &c); + void Post(const OmpDefaultmapClause::VariableCategory &c); + void Post(const OmpScheduleModifierType::ModType &c); + void Post(const OmpLinearModifier::Type &c); + void Post(const OmpDependenceType::Type &c); + void Post(const OmpMapType::Type &c); + void Post(const OmpScheduleClause::ScheduleType &c); + void Post(const OmpIfClause::DirectiveNameModifier &c); + void Post(const OmpCancelType::Type &c); + void Post(const OmpClause &c); + void PostClauseCommon(const ClauseInfo &ci); + void Post(const DoConstruct &); + + std::string clauseDetails{""}; + std::map, int> constructClauseCount; + + // curLoopLogRecord and loopLogRecordStack store + // pointers to this datastructure's entries. Hence a + // vector cannot be used since pointers are invalidated + // on resize. Next best option seems to be deque. Also a + // list cannot be used since YAML gen requires a + // datastructure which can be accessed through indices. + std::deque constructClauses; + + LogRecord *curLoopLogRecord{nullptr}; + std::vector loopLogRecordStack; + std::vector ompWrapperStack; + std::map> clauseStrings; + Parsing *parsing{nullptr}; +}; +} // namespace parser +} // namespace Fortran + +#endif /* FORTRAN_FLANG_OMP_REPORT_VISITOR_H */ diff --git a/flang/examples/flang-omp-report-plugin/flang-omp-report-visitor.cpp b/flang/examples/flang-omp-report-plugin/flang-omp-report-visitor.cpp new file mode 100644 --- /dev/null +++ b/flang/examples/flang-omp-report-plugin/flang-omp-report-visitor.cpp @@ -0,0 +1,269 @@ +//===-- examples/flang-omp-report-plugin/flang-omp-report-visitor.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 "flang-omp-report-visitor.h" + +namespace Fortran { +namespace parser { +bool operator<(const ClauseInfo &a, const ClauseInfo &b) { + return a.clause < b.clause; +} +bool operator==(const ClauseInfo &a, const ClauseInfo &b) { + return a.clause == b.clause && a.clauseDetails == b.clauseDetails; +} +bool operator!=(const ClauseInfo &a, const ClauseInfo &b) { return !(a == b); } + +bool operator==(const LogRecord &a, const LogRecord &b) { + return a.file == b.file && a.line == b.line && a.construct == b.construct && + a.clauses == b.clauses; +} +bool operator!=(const LogRecord &a, const LogRecord &b) { return !(a == b); } + +std::string OpenMPCounterVisitor::normalize_construct_name(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c) { return std::tolower(c); }); + return s; +} +ClauseInfo OpenMPCounterVisitor::normalize_clause_name(const std::string &s) { + std::size_t start = s.find('('); + std::size_t end = s.find(')'); + std::string clauseName; + if (start != std::string::npos && end != std::string::npos) { + clauseName = s.substr(0, start); + clauseDetails = s.substr(start + 1, end - start - 1); + } else { + clauseName = s; + } + std::transform(clauseName.begin(), clauseName.end(), clauseName.begin(), + [](unsigned char c) { return std::tolower(c); }); + std::transform(clauseDetails.begin(), clauseDetails.end(), + clauseDetails.begin(), [](unsigned char c) { return std::tolower(c); }); + return ClauseInfo{clauseName, clauseDetails}; +} +SourcePosition OpenMPCounterVisitor::getLocation(const OmpWrapperType &w) { + if (auto *val = std::get_if(&w)) { + const OpenMPConstruct *o{*val}; + return getLocation(*o); + } + return getLocation(*std::get(w)); +} +SourcePosition OpenMPCounterVisitor::getLocation( + const OpenMPDeclarativeConstruct &c) { + return std::visit( + [&](const auto &o) -> SourcePosition { + return parsing->allCooked().GetSourcePositionRange(o.source)->first; + }, + c.u); +} +SourcePosition OpenMPCounterVisitor::getLocation(const OpenMPConstruct &c) { + return std::visit( + Fortran::common::visitors{ + [&](const OpenMPStandaloneConstruct &c) -> SourcePosition { + return parsing->allCooked().GetSourcePositionRange(c.source)->first; + }, + // OpenMPSectionsConstruct, OpenMPLoopConstruct, + // OpenMPBlockConstruct, OpenMPCriticalConstruct Get the source from + // the directive field. + [&](const auto &c) -> SourcePosition { + const CharBlock &source{std::get<0>(c.t).source}; + return (parsing->allCooked().GetSourcePositionRange(source))->first; + }, + [&](const OpenMPAtomicConstruct &c) -> SourcePosition { + return std::visit( + [&](const auto &o) -> SourcePosition { + const CharBlock &source{std::get(o.t).source}; + return parsing->allCooked() + .GetSourcePositionRange(source) + ->first; + }, + c.u); + }, + }, + c.u); +} + +std::string OpenMPCounterVisitor::getName(const OmpWrapperType &w) { + if (auto *val = std::get_if(&w)) { + const OpenMPConstruct *o{*val}; + return getName(*o); + } + return getName(*std::get(w)); +} +std::string OpenMPCounterVisitor::getName(const OpenMPDeclarativeConstruct &c) { + return std::visit( + [&](const auto &o) -> std::string { + const CharBlock &source{std::get(o.t).source}; + return normalize_construct_name(source.ToString()); + }, + c.u); +} +std::string OpenMPCounterVisitor::getName(const OpenMPConstruct &c) { + return std::visit( + Fortran::common::visitors{ + [&](const OpenMPStandaloneConstruct &c) -> std::string { + return std::visit( + [&](const auto &c) { + // Get source from the directive or verbatim fields + const CharBlock &source{std::get<0>(c.t).source}; + return normalize_construct_name(source.ToString()); + }, + c.u); + }, + [&](const OpenMPExecutableAllocate &c) -> std::string { + const CharBlock &source{std::get<0>(c.t).source}; + return normalize_construct_name(source.ToString()); + }, + [&](const OpenMPDeclarativeAllocate &c) -> std::string { + const CharBlock &source{std::get<0>(c.t).source}; + return normalize_construct_name(source.ToString()); + }, + [&](const OpenMPAtomicConstruct &c) -> std::string { + return std::visit( + [&](const auto &c) { + // Get source from the verbatim fields + const CharBlock &source{std::get(c.t).source}; + return "atomic-" + + normalize_construct_name(source.ToString()); + }, + c.u); + }, + // OpenMPSectionsConstruct, OpenMPLoopConstruct, + // OpenMPBlockConstruct, OpenMPCriticalConstruct Get the source from + // the directive field of the begin directive or from the verbatim + // field of the begin directive in Critical + [&](const auto &c) -> std::string { + const CharBlock &source{std::get<0>(std::get<0>(c.t).t).source}; + return normalize_construct_name(source.ToString()); + }, + }, + c.u); +} + +bool OpenMPCounterVisitor::Pre(const OpenMPDeclarativeConstruct &c) { + OmpWrapperType *ow{new OmpWrapperType(&c)}; + ompWrapperStack.push_back(ow); + return true; +} +bool OpenMPCounterVisitor::Pre(const OpenMPConstruct &c) { + OmpWrapperType *ow{new OmpWrapperType(&c)}; + ompWrapperStack.push_back(ow); + return true; +} +bool OpenMPCounterVisitor::Pre(const OmpEndLoopDirective &c) { return true; } +bool OpenMPCounterVisitor::Pre(const DoConstruct &) { + loopLogRecordStack.push_back(curLoopLogRecord); + return true; +} + +void OpenMPCounterVisitor::Post(const OpenMPDeclarativeConstruct &) { + PostConstructsCommon(); +} +void OpenMPCounterVisitor::Post(const OpenMPConstruct &) { + PostConstructsCommon(); +} +void OpenMPCounterVisitor::PostConstructsCommon() { + OmpWrapperType *curConstruct = ompWrapperStack.back(); + std::sort( + clauseStrings[curConstruct].begin(), clauseStrings[curConstruct].end()); + + SourcePosition s{getLocation(*curConstruct)}; + LogRecord r{s.file.path(), s.line, getName(*curConstruct), + clauseStrings[curConstruct]}; + constructClauses.push_back(r); + + // Keep track of loop log records if it can potentially have the + // nowait clause added on later. + if (const auto *oc = std::get_if(curConstruct)) { + if (const auto *olc = std::get_if(&(*oc)->u)) { + const auto &beginLoopDir{ + std::get(olc->t)}; + const auto &beginDir{ + std::get(beginLoopDir.t)}; + if (beginDir.v == llvm::omp::Directive::OMPD_do || + beginDir.v == llvm::omp::Directive::OMPD_do_simd) { + curLoopLogRecord = &constructClauses.back(); + } + } + } + + auto it = clauseStrings.find(curConstruct); + clauseStrings.erase(it); + ompWrapperStack.pop_back(); + delete curConstruct; +} +void OpenMPCounterVisitor::Post(const OmpEndLoopDirective &c) {} + +void OpenMPCounterVisitor::Post(const OmpProcBindClause::Type &c) { + clauseDetails += "type=" + OmpProcBindClause::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpDefaultClause::Type &c) { + clauseDetails += "type=" + OmpDefaultClause::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post( + const OmpDefaultmapClause::ImplicitBehavior &c) { + clauseDetails += + "implicit_behavior=" + OmpDefaultmapClause::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post( + const OmpDefaultmapClause::VariableCategory &c) { + clauseDetails += + "variable_category=" + OmpDefaultmapClause::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpScheduleModifierType::ModType &c) { + clauseDetails += "modifier=" + OmpScheduleModifierType::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpLinearModifier::Type &c) { + clauseDetails += "modifier=" + OmpLinearModifier::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpDependenceType::Type &c) { + clauseDetails += "type=" + OmpDependenceType::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpMapType::Type &c) { + clauseDetails += "type=" + OmpMapType::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpScheduleClause::ScheduleType &c) { + clauseDetails += "type=" + OmpScheduleClause::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpIfClause::DirectiveNameModifier &c) { + clauseDetails += "name_modifier=" + OmpIfClause::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpCancelType::Type &c) { + clauseDetails += "type=" + OmpCancelType::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpClause &c) { + PostClauseCommon(normalize_clause_name(c.source.ToString())); + clauseDetails.clear(); +} +void OpenMPCounterVisitor::PostClauseCommon(const ClauseInfo &ci) { + // The end loop construct (!$omp end do) can contain a nowait clause. + // The flang parser does not parse the end loop construct as part of + // the OpenMP construct for the loop construct. So the end loop is left + // hanging as a separate executable construct. If a nowait clause is seen in + // an end loop construct we have to find the associated loop construct and + // add nowait to its list of clauses. Note: This is not a bug in flang, the + // parse tree is corrected during semantic analysis. + if (ci.clause == "nowait") { + assert(curLoopLogRecord && + "loop Construct should be visited before a nowait clause"); + constructClauseCount[std::make_pair( + curLoopLogRecord->construct, ci.clause)]++; + curLoopLogRecord->clauses.push_back(ci); + } else { + assert(!ompWrapperStack.empty() && + "Construct should be visited before clause"); + constructClauseCount[std::make_pair( + getName(*ompWrapperStack.back()), ci.clause)]++; + clauseStrings[ompWrapperStack.back()].push_back(ci); + } +} +void OpenMPCounterVisitor::Post(const DoConstruct &) { + curLoopLogRecord = loopLogRecordStack.back(); + loopLogRecordStack.pop_back(); +} +} // namespace parser +} // namespace Fortran diff --git a/flang/examples/flang-omp-report-plugin/flang-omp-report.cpp b/flang/examples/flang-omp-report-plugin/flang-omp-report.cpp new file mode 100644 --- /dev/null +++ b/flang/examples/flang-omp-report-plugin/flang-omp-report.cpp @@ -0,0 +1,79 @@ +//===-- examples/flang-omp-report-plugin/flang-omp-report.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 +// +//===----------------------------------------------------------------------===// +// This plugin parses a Fortran source file and generates a YAML +// report with all the OpenMP constructs and clauses and which +// line they're located on. +// +// The plugin may be invoked as: +// ./bin/flang-new -fc1 -load lib/flangOmpReport.so -plugin +// flang-omp-report -fopenmp -o - +// +//===----------------------------------------------------------------------===// + +#include "flang-omp-report-visitor.h" + +#include "flang/Frontend/CompilerInstance.h" +#include "flang/Frontend/FrontendActions.h" +#include "flang/Frontend/FrontendPluginRegistry.h" +#include "flang/Parser/dump-parse-tree.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace Fortran::frontend; +using namespace Fortran::parser; + +LLVM_YAML_IS_SEQUENCE_VECTOR(LogRecord) +LLVM_YAML_IS_SEQUENCE_VECTOR(ClauseInfo) +namespace llvm { +namespace yaml { +using llvm::yaml::IO; +using llvm::yaml::MappingTraits; +template +struct SequenceTraits, + std::enable_if_t::flow>::value>> + : SequenceTraitsImpl, SequenceElementTraits::flow> {}; +template <> struct MappingTraits { + static void mapping(IO &io, ClauseInfo &info) { + io.mapRequired("clause", info.clause); + io.mapRequired("details", info.clauseDetails); + } +}; +template <> struct MappingTraits { + static void mapping(IO &io, LogRecord &info) { + io.mapRequired("file", info.file); + io.mapRequired("line", info.line); + io.mapRequired("construct", info.construct); + io.mapRequired("clauses", info.clauses); + } +}; +} // namespace yaml +} // namespace llvm + +class FlangOmpReport : public PluginParseTreeAction { + void ExecuteAction() override { + // Prepare the parse tree and the visitor + CompilerInstance &ci = this->instance(); + Parsing &parsing = ci.parsing(); + const Program &parseTree = *parsing.parseTree(); + OpenMPCounterVisitor visitor; + visitor.parsing = &parsing; + + // Walk the parse tree + Walk(parseTree, visitor); + + // Dump the output + std::unique_ptr OS{ci.CreateDefaultOutputFile( + /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(), + /*Extension=*/".yaml")}; + llvm::yaml::Output yout(*OS); + yout << visitor.constructClauses; + } +}; + +static FrontendPluginRegistry::Add X("flang-omp-report", + "Generate a YAML summary of OpenMP constructs and clauses"); diff --git a/flang/test/Examples/omp-atomic.f90 b/flang/test/Examples/omp-atomic.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Examples/omp-atomic.f90 @@ -0,0 +1,59 @@ +! Check the flang-omp-report plugin for omp-atomic.f90 + +! REQUIRES: plugins + +! RUN: %flang_fc1 -load %llvmshlibdir/flangOmpReport.so -plugin flang-omp-report -fopenmp %s -o - | FileCheck %s + +! Check OpenMP 2.13.6 atomic Construct + + a = 1.0 + !$omp parallel num_threads(4) shared(a) + !$omp atomic seq_cst, read + b = a + + !$omp atomic seq_cst write + a = b + !$omp end atomic + + !$omp atomic capture seq_cst + b = a + a = a + 1 + !$omp end atomic + + !$omp atomic + a = a + 1 + !$omp end parallel +end + +! CHECK:--- +! CHECK-NEXT:- file: '{{[^"]*}}omp-atomic.f90' +! CHECK-NEXT: line: 11 +! CHECK-NEXT: construct: atomic-read +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: seq_cst +! CHECK-NEXT: details: '' +! CHECK-NEXT:- file: '{{[^"]*}}omp-atomic.f90' +! CHECK-NEXT: line: 14 +! CHECK-NEXT: construct: atomic-write +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: seq_cst +! CHECK-NEXT: details: '' +! CHECK-NEXT:- file: '{{[^"]*}}omp-atomic.f90' +! CHECK-NEXT: line: 18 +! CHECK-NEXT: construct: atomic-capture +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: seq_cst +! CHECK-NEXT: details: '' +! CHECK-NEXT:- file: '{{[^"]*}}omp-atomic.f90' +! CHECK-NEXT: line: 23 +! CHECK-NEXT: construct: atomic-atomic +! CHECK-NEXT: clauses: [] +! CHECK-NEXT:- file: '{{[^"]*}}omp-atomic.f90' +! CHECK-NEXT: line: 10 +! CHECK-NEXT: construct: parallel +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: num_threads +! CHECK-NEXT: details: '4' +! CHECK-NEXT: - clause: shared +! CHECK-NEXT: details: a +! CHECK-NEXT:... diff --git a/flang/test/Examples/omp-declarative-directive.f90 b/flang/test/Examples/omp-declarative-directive.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Examples/omp-declarative-directive.f90 @@ -0,0 +1,43 @@ +! Check the flang-omp-report plugin for omp-declarative-directive.f90 + +! REQUIRES: plugins + +! RUN: %flang_fc1 -load %llvmshlibdir/flangOmpReport.so -plugin flang-omp-report -fopenmp %s -o - | FileCheck %s + +! Check OpenMP declarative directives + +! 2.8.2 declare-simd + +subroutine declare_simd_1(a, b) + real(8), intent(inout) :: a, b + !$omp declare simd(declare_simd_1) aligned(a) + a = 3.14 + b +end subroutine declare_simd_1 + +! 2.10.6 declare-target +! 2.15.2 threadprivate + +module m2 +contains + subroutine foo + !$omp declare target + integer, parameter :: N=10000, M=1024 + integer :: i + real :: Q(N, N), R(N,M), S(M,M) + end subroutine foo +end module m2 + +end + +! CHECK:--- +! CHECK-NEXT:- file: '{{[^"]*}}omp-declarative-directive.f90' +! CHECK-NEXT: line: 13 +! CHECK-NEXT: construct: declare simd +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: aligned +! CHECK-NEXT: details: a +! CHECK-NEXT:- file: '{{[^"]*}}omp-declarative-directive.f90' +! CHECK-NEXT: line: 23 +! CHECK-NEXT: construct: declare target +! CHECK-NEXT: clauses: [] +! CHECK-NEXT:... diff --git a/flang/test/Examples/omp-device-constructs.f90 b/flang/test/Examples/omp-device-constructs.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Examples/omp-device-constructs.f90 @@ -0,0 +1,103 @@ +! Check flang-omp-report --femit-yaml for omp-device-constructs.f90 + +! REQUIRES: plugins + +!RUN: %flang_fc1 -load %llvmshlibdir/flangOmpReport.so -plugin flang-omp-report -fopenmp %s -o - | FileCheck %s + +! Check OpenMP clause validity for the following directives: +! 2.10 Device constructs +program main + + real(8) :: arrayA(256), arrayB(256) + integer :: N + + arrayA = 1.414 + arrayB = 3.14 + N = 256 + + !$omp target map(arrayA) + do i = 1, N + a = 3.14 + enddo + !$omp end target + + !$omp target device(0) + do i = 1, N + a = 3.14 + enddo + !$omp end target + + !$omp target defaultmap(tofrom:scalar) + do i = 1, N + a = 3.14 + enddo + !$omp end target + + !$omp teams num_teams(3) thread_limit(10) default(shared) private(i) shared(a) + do i = 1, N + a = 3.14 + enddo + !$omp end teams + + !$omp target map(tofrom:a) + do i = 1, N + a = 3.14 + enddo + !$omp end target + + !$omp target data device(0) map(to:a) + do i = 1, N + a = 3.14 + enddo + !$omp end target data + +end program main + +! CHECK: --- +! CHECK-NEXT: - file: '{{[^"]*}}omp-device-constructs.f90' +! CHECK-NEXT: line: 18 +! CHECK-NEXT: construct: target +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: map +! CHECK-NEXT: details: arraya +! CHECK-NEXT: - file: '{{[^"]*}}omp-device-constructs.f90' +! CHECK-NEXT: line: 24 +! CHECK-NEXT: construct: target +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: device +! CHECK-NEXT: details: '0' +! CHECK-NEXT: - file: '{{[^"]*}}omp-device-constructs.f90' +! CHECK-NEXT: line: 30 +! CHECK-NEXT: construct: target +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: defaultmap +! CHECK-NEXT: details: 'tofrom:scalar' +! CHECK-NEXT: - file: '{{[^"]*}}omp-device-constructs.f90' +! CHECK-NEXT: line: 36 +! CHECK-NEXT: construct: teams +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: default +! CHECK-NEXT: details: shared +! CHECK-NEXT: - clause: num_teams +! CHECK-NEXT: details: '3' +! CHECK-NEXT: - clause: private +! CHECK-NEXT: details: i +! CHECK-NEXT: - clause: shared +! CHECK-NEXT: details: a +! CHECK-NEXT: - clause: thread_limit +! CHECK-NEXT: details: '10' +! CHECK-NEXT: - file: '{{[^"]*}}omp-device-constructs.f90' +! CHECK-NEXT: line: 42 +! CHECK-NEXT: construct: target +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: map +! CHECK-NEXT: details: 'tofrom:a' +! CHECK-NEXT: - file: '{{[^"]*}}omp-device-constructs.f90' +! CHECK-NEXT: line: 48 +! CHECK-NEXT: construct: target data +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: device +! CHECK-NEXT: details: '0' +! CHECK-NEXT: - clause: map +! CHECK-NEXT: details: 'to:a' +! CHECK-NEXT: ...