brintos

brintos / llvm-project-archived public Read only

0
0
Text · 46.0 KiB · 23c3c4d Raw
1216 lines · cpp
1//===- llvm/unittests/Frontend/OpenMPDecompositionTest.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/ADT/ArrayRef.h"10#include "llvm/ADT/STLExtras.h"11#include "llvm/ADT/SmallVector.h"12#include "llvm/Frontend/OpenMP/ClauseT.h"13#include "llvm/Frontend/OpenMP/ConstructDecompositionT.h"14#include "llvm/Frontend/OpenMP/OMP.h"15#include "gtest/gtest.h"16 17#include <iterator>18#include <optional>19#include <sstream>20#include <string>21#include <tuple>22#include <type_traits>23#include <utility>24 25// The actual tests start at comment "--- Test" below.26 27// Create simple instantiations of all clauses to allow manual construction28// of clauses, and implement emitting of a directive with clauses to a string.29//30// The tests then follow the pattern31// 1. Create a list of clauses.32// 2. Pass them, together with a construct, to the decomposition class.33// 3. Extract individual resulting leaf constructs with clauses applied34//    to them.35// 4. Convert them to strings and compare with expected outputs.36 37namespace omp {38struct TypeTy {}; // placeholder39struct ExprTy {}; // placeholder40using IdTy = std::string;41} // namespace omp42 43namespace tomp::type {44template <> struct ObjectT<omp::IdTy, omp::ExprTy> {45  const omp::IdTy &id() const { return name; }46  const std::optional<omp::ExprTy> ref() const { return omp::ExprTy{}; }47 48  omp::IdTy name;49};50} // namespace tomp::type51 52namespace omp {53template <typename ElemTy> using List = tomp::type::ListT<ElemTy>;54 55using Object = tomp::ObjectT<IdTy, ExprTy>;56 57namespace clause {58using DefinedOperator = tomp::type::DefinedOperatorT<IdTy, ExprTy>;59using ProcedureDesignator = tomp::type::ProcedureDesignatorT<IdTy, ExprTy>;60using ReductionOperator = tomp::type::ReductionIdentifierT<IdTy, ExprTy>;61 62using AcqRel = tomp::clause::AcqRelT<TypeTy, IdTy, ExprTy>;63using Acquire = tomp::clause::AcquireT<TypeTy, IdTy, ExprTy>;64using AdjustArgs = tomp::clause::AdjustArgsT<TypeTy, IdTy, ExprTy>;65using Affinity = tomp::clause::AffinityT<TypeTy, IdTy, ExprTy>;66using Aligned = tomp::clause::AlignedT<TypeTy, IdTy, ExprTy>;67using Align = tomp::clause::AlignT<TypeTy, IdTy, ExprTy>;68using Allocate = tomp::clause::AllocateT<TypeTy, IdTy, ExprTy>;69using Allocator = tomp::clause::AllocatorT<TypeTy, IdTy, ExprTy>;70using AppendArgs = tomp::clause::AppendArgsT<TypeTy, IdTy, ExprTy>;71using AtomicDefaultMemOrder =72    tomp::clause::AtomicDefaultMemOrderT<TypeTy, IdTy, ExprTy>;73using At = tomp::clause::AtT<TypeTy, IdTy, ExprTy>;74using Bind = tomp::clause::BindT<TypeTy, IdTy, ExprTy>;75using Capture = tomp::clause::CaptureT<TypeTy, IdTy, ExprTy>;76using Collapse = tomp::clause::CollapseT<TypeTy, IdTy, ExprTy>;77using Compare = tomp::clause::CompareT<TypeTy, IdTy, ExprTy>;78using Copyin = tomp::clause::CopyinT<TypeTy, IdTy, ExprTy>;79using Copyprivate = tomp::clause::CopyprivateT<TypeTy, IdTy, ExprTy>;80using Defaultmap = tomp::clause::DefaultmapT<TypeTy, IdTy, ExprTy>;81using Default = tomp::clause::DefaultT<TypeTy, IdTy, ExprTy>;82using Depend = tomp::clause::DependT<TypeTy, IdTy, ExprTy>;83using Destroy = tomp::clause::DestroyT<TypeTy, IdTy, ExprTy>;84using Detach = tomp::clause::DetachT<TypeTy, IdTy, ExprTy>;85using Device = tomp::clause::DeviceT<TypeTy, IdTy, ExprTy>;86using DeviceType = tomp::clause::DeviceTypeT<TypeTy, IdTy, ExprTy>;87using DistSchedule = tomp::clause::DistScheduleT<TypeTy, IdTy, ExprTy>;88using Doacross = tomp::clause::DoacrossT<TypeTy, IdTy, ExprTy>;89using DynamicAllocators =90    tomp::clause::DynamicAllocatorsT<TypeTy, IdTy, ExprTy>;91using DynGroupprivate = tomp::clause::DynGroupprivateT<TypeTy, IdTy, ExprTy>;92using Enter = tomp::clause::EnterT<TypeTy, IdTy, ExprTy>;93using Exclusive = tomp::clause::ExclusiveT<TypeTy, IdTy, ExprTy>;94using Fail = tomp::clause::FailT<TypeTy, IdTy, ExprTy>;95using Filter = tomp::clause::FilterT<TypeTy, IdTy, ExprTy>;96using Final = tomp::clause::FinalT<TypeTy, IdTy, ExprTy>;97using Firstprivate = tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>;98using From = tomp::clause::FromT<TypeTy, IdTy, ExprTy>;99using Full = tomp::clause::FullT<TypeTy, IdTy, ExprTy>;100using Grainsize = tomp::clause::GrainsizeT<TypeTy, IdTy, ExprTy>;101using HasDeviceAddr = tomp::clause::HasDeviceAddrT<TypeTy, IdTy, ExprTy>;102using Hint = tomp::clause::HintT<TypeTy, IdTy, ExprTy>;103using If = tomp::clause::IfT<TypeTy, IdTy, ExprTy>;104using Inbranch = tomp::clause::InbranchT<TypeTy, IdTy, ExprTy>;105using Inclusive = tomp::clause::InclusiveT<TypeTy, IdTy, ExprTy>;106using Indirect = tomp::clause::IndirectT<TypeTy, IdTy, ExprTy>;107using Init = tomp::clause::InitT<TypeTy, IdTy, ExprTy>;108using InReduction = tomp::clause::InReductionT<TypeTy, IdTy, ExprTy>;109using IsDevicePtr = tomp::clause::IsDevicePtrT<TypeTy, IdTy, ExprTy>;110using Lastprivate = tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy>;111using Linear = tomp::clause::LinearT<TypeTy, IdTy, ExprTy>;112using Link = tomp::clause::LinkT<TypeTy, IdTy, ExprTy>;113using Map = tomp::clause::MapT<TypeTy, IdTy, ExprTy>;114using Match = tomp::clause::MatchT<TypeTy, IdTy, ExprTy>;115using Mergeable = tomp::clause::MergeableT<TypeTy, IdTy, ExprTy>;116using Message = tomp::clause::MessageT<TypeTy, IdTy, ExprTy>;117using Nocontext = tomp::clause::NocontextT<TypeTy, IdTy, ExprTy>;118using Nogroup = tomp::clause::NogroupT<TypeTy, IdTy, ExprTy>;119using Nontemporal = tomp::clause::NontemporalT<TypeTy, IdTy, ExprTy>;120using Notinbranch = tomp::clause::NotinbranchT<TypeTy, IdTy, ExprTy>;121using Novariants = tomp::clause::NovariantsT<TypeTy, IdTy, ExprTy>;122using Nowait = tomp::clause::NowaitT<TypeTy, IdTy, ExprTy>;123using NumTasks = tomp::clause::NumTasksT<TypeTy, IdTy, ExprTy>;124using NumTeams = tomp::clause::NumTeamsT<TypeTy, IdTy, ExprTy>;125using NumThreads = tomp::clause::NumThreadsT<TypeTy, IdTy, ExprTy>;126using OmpxAttribute = tomp::clause::OmpxAttributeT<TypeTy, IdTy, ExprTy>;127using OmpxBare = tomp::clause::OmpxBareT<TypeTy, IdTy, ExprTy>;128using OmpxDynCgroupMem = tomp::clause::OmpxDynCgroupMemT<TypeTy, IdTy, ExprTy>;129using Ordered = tomp::clause::OrderedT<TypeTy, IdTy, ExprTy>;130using Order = tomp::clause::OrderT<TypeTy, IdTy, ExprTy>;131using Partial = tomp::clause::PartialT<TypeTy, IdTy, ExprTy>;132using Priority = tomp::clause::PriorityT<TypeTy, IdTy, ExprTy>;133using Private = tomp::clause::PrivateT<TypeTy, IdTy, ExprTy>;134using ProcBind = tomp::clause::ProcBindT<TypeTy, IdTy, ExprTy>;135using Read = tomp::clause::ReadT<TypeTy, IdTy, ExprTy>;136using Reduction = tomp::clause::ReductionT<TypeTy, IdTy, ExprTy>;137using Relaxed = tomp::clause::RelaxedT<TypeTy, IdTy, ExprTy>;138using Release = tomp::clause::ReleaseT<TypeTy, IdTy, ExprTy>;139using ReverseOffload = tomp::clause::ReverseOffloadT<TypeTy, IdTy, ExprTy>;140using Safelen = tomp::clause::SafelenT<TypeTy, IdTy, ExprTy>;141using Schedule = tomp::clause::ScheduleT<TypeTy, IdTy, ExprTy>;142using SeqCst = tomp::clause::SeqCstT<TypeTy, IdTy, ExprTy>;143using Severity = tomp::clause::SeverityT<TypeTy, IdTy, ExprTy>;144using Shared = tomp::clause::SharedT<TypeTy, IdTy, ExprTy>;145using Simdlen = tomp::clause::SimdlenT<TypeTy, IdTy, ExprTy>;146using Simd = tomp::clause::SimdT<TypeTy, IdTy, ExprTy>;147using Sizes = tomp::clause::SizesT<TypeTy, IdTy, ExprTy>;148using TaskReduction = tomp::clause::TaskReductionT<TypeTy, IdTy, ExprTy>;149using ThreadLimit = tomp::clause::ThreadLimitT<TypeTy, IdTy, ExprTy>;150using Threads = tomp::clause::ThreadsT<TypeTy, IdTy, ExprTy>;151using To = tomp::clause::ToT<TypeTy, IdTy, ExprTy>;152using UnifiedAddress = tomp::clause::UnifiedAddressT<TypeTy, IdTy, ExprTy>;153using UnifiedSharedMemory =154    tomp::clause::UnifiedSharedMemoryT<TypeTy, IdTy, ExprTy>;155using Uniform = tomp::clause::UniformT<TypeTy, IdTy, ExprTy>;156using Unknown = tomp::clause::UnknownT<TypeTy, IdTy, ExprTy>;157using Untied = tomp::clause::UntiedT<TypeTy, IdTy, ExprTy>;158using Update = tomp::clause::UpdateT<TypeTy, IdTy, ExprTy>;159using UseDeviceAddr = tomp::clause::UseDeviceAddrT<TypeTy, IdTy, ExprTy>;160using UseDevicePtr = tomp::clause::UseDevicePtrT<TypeTy, IdTy, ExprTy>;161using UsesAllocators = tomp::clause::UsesAllocatorsT<TypeTy, IdTy, ExprTy>;162using Use = tomp::clause::UseT<TypeTy, IdTy, ExprTy>;163using Weak = tomp::clause::WeakT<TypeTy, IdTy, ExprTy>;164using When = tomp::clause::WhenT<TypeTy, IdTy, ExprTy>;165using Write = tomp::clause::WriteT<TypeTy, IdTy, ExprTy>;166} // namespace clause167 168struct Helper {169  std::optional<Object> getBaseObject(const Object &object) {170    return std::nullopt;171  }172  std::optional<Object> getLoopIterVar() { return std::nullopt; }173};174 175using Clause = tomp::ClauseT<TypeTy, IdTy, ExprTy>;176using ConstructDecomposition = tomp::ConstructDecompositionT<Clause, Helper>;177using DirectiveWithClauses = tomp::DirectiveWithClauses<Clause>;178} // namespace omp179 180struct StringifyClause {181  static std::string join(const omp::List<std::string> &Strings) {182    std::stringstream Stream;183    for (const auto &[Index, String] : llvm::enumerate(Strings)) {184      if (Index != 0)185        Stream << ", ";186      Stream << String;187    }188    return Stream.str();189  }190 191  static std::string to_str(llvm::omp::Directive D) {192    return getOpenMPDirectiveName(D, llvm::omp::FallbackVersion).str();193  }194  static std::string to_str(llvm::omp::Clause C) {195    return getOpenMPClauseName(C).str();196  }197  static std::string to_str(const omp::TypeTy &Type) { return "type"; }198  static std::string to_str(const omp::ExprTy &Expr) { return "expr"; }199  static std::string to_str(const omp::Object &Obj) { return Obj.id(); }200 201  template <typename U>202  static std::enable_if_t<std::is_enum_v<llvm::remove_cvref_t<U>>, std::string>203  to_str(U &&Item) {204    return std::to_string(llvm::to_underlying(Item));205  }206 207  template <typename U> static std::string to_str(const omp::List<U> &Items) {208    omp::List<std::string> Names;209    llvm::transform(Items, std::back_inserter(Names),210                    [](auto &&S) { return to_str(S); });211    return "(" + join(Names) + ")";212  }213 214  template <typename U>215  static std::string to_str(const std::optional<U> &Item) {216    if (Item)217      return to_str(*Item);218    return "";219  }220 221  template <typename... Us, size_t... Is>222  static std::string to_str(const std::tuple<Us...> &Tuple,223                            std::index_sequence<Is...>) {224    omp::List<std::string> Strings;225    (Strings.push_back(to_str(std::get<Is>(Tuple))), ...);226    return "(" + join(Strings) + ")";227  }228 229  template <typename U>230  static std::enable_if_t<llvm::remove_cvref_t<U>::EmptyTrait::value,231                          std::string>232  to_str(U &&Item) {233    return "";234  }235 236  template <typename U>237  static std::enable_if_t<llvm::remove_cvref_t<U>::IncompleteTrait::value,238                          std::string>239  to_str(U &&Item) {240    return "";241  }242 243  template <typename U>244  static std::enable_if_t<llvm::remove_cvref_t<U>::WrapperTrait::value,245                          std::string>246  to_str(U &&Item) {247    // For a wrapper, stringify the wrappee, and only add parentheses if248    // there aren't any already.249    std::string Str = to_str(Item.v);250    if (!Str.empty()) {251      if (Str.front() == '(' && Str.back() == ')')252        return Str;253    }254    return "(" + to_str(Item.v) + ")";255  }256 257  template <typename U>258  static std::enable_if_t<llvm::remove_cvref_t<U>::TupleTrait::value,259                          std::string>260  to_str(U &&Item) {261    constexpr size_t TupleSize =262        std::tuple_size_v<llvm::remove_cvref_t<decltype(Item.t)>>;263    return to_str(Item.t, std::make_index_sequence<TupleSize>{});264  }265 266  template <typename U>267  static std::enable_if_t<llvm::remove_cvref_t<U>::UnionTrait::value,268                          std::string>269  to_str(U &&Item) {270    return std::visit([](auto &&S) { return to_str(S); }, Item.u);271  }272 273  StringifyClause(const omp::Clause &C)274      // Rely on content stringification to emit enclosing parentheses.275      : Str(to_str(C.id) + to_str(C)) {}276 277  std::string Str;278};279 280std::string stringify(const omp::Clause &C) { //281  return StringifyClause(C).Str;282}283 284std::string stringify(const omp::DirectiveWithClauses &DWC) {285  std::stringstream Stream;286 287  Stream << getOpenMPDirectiveName(DWC.id, llvm::omp::FallbackVersion).str();288  for (const omp::Clause &C : DWC.clauses)289    Stream << ' ' << stringify(C);290 291  return Stream.str();292}293 294std::string stringify(tomp::ErrorCode E) {295  switch (E) {296  case tomp::ErrorCode::NoLeafAllowing:297    return "no leaf that allows this clause";298  case tomp::ErrorCode::NoLeafPrivatizing:299    return "no leaf with a privatizing clause";300  case tomp::ErrorCode::InvalidDirNameMod:301    return "invalid directive name modifier";302  case tomp::ErrorCode::RedModNotApplied:303    return "the reduction modifier cannot be applied";304  }305  return "unrecognized error code " + std::to_string(llvm::to_underlying(E));306}307 308std::string stringify(std::pair<const omp::Clause *, tomp::ErrorCode> &ER) {309  std::stringstream Stream;310 311  Stream << "error while applying '" << stringify(*ER.first)312         << "': " << stringify(ER.second);313  return Stream.str();314}315 316// --- Tests ----------------------------------------------------------317 318namespace red {319// Make it easier to construct reduction operators from built-in intrinsics.320omp::clause::ReductionOperator321makeOp(omp::clause::DefinedOperator::IntrinsicOperator Op) {322  return omp::clause::ReductionOperator{omp::clause::DefinedOperator{Op}};323}324} // namespace red325 326namespace {327using namespace llvm::omp;328 329class OpenMPDecompositionTest : public testing::Test {330protected:331  void SetUp() override {}332  void TearDown() override {}333 334  omp::Helper Helper;335  uint32_t AnyVersion = 999;336};337 338// PRIVATE339// [5.2:111:5-7]340// Directives: distribute, do, for, loop, parallel, scope, sections, simd,341// single, target, task, taskloop, teams342//343// [5.2:340:1-2]344// (1) The effect of the 1 private clause is as if it is applied only to the345// innermost leaf construct that permits it.346TEST_F(OpenMPDecompositionTest, Private1) {347  omp::Object x{"x"};348 349  omp::List<omp::Clause> Clauses{350      {OMPC_private, omp::clause::Private{{x}}},351  };352 353  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections,354                                  Clauses);355  ASSERT_EQ(Dec.output.size(), 2u);356 357  std::string Dir0 = stringify(Dec.output[0]);358  std::string Dir1 = stringify(Dec.output[1]);359  ASSERT_EQ(Dir0, "parallel");            // (1)360  ASSERT_EQ(Dir1, "sections private(x)"); // (1)361}362 363TEST_F(OpenMPDecompositionTest, Private2) {364  omp::Object x{"x"};365 366  omp::List<omp::Clause> Clauses{367      {OMPC_private, omp::clause::Private{{x}}},368  };369 370  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_masked,371                                  Clauses);372  ASSERT_EQ(Dec.output.size(), 2u);373 374  std::string Dir0 = stringify(Dec.output[0]);375  std::string Dir1 = stringify(Dec.output[1]);376  ASSERT_EQ(Dir0, "parallel private(x)"); // (1)377  ASSERT_EQ(Dir1, "masked");              // (1)378}379 380// FIRSTPRIVATE381// [5.2:112:5-7]382// Directives: distribute, do, for, parallel, scope, sections, single, target,383// task, taskloop, teams384//385// [5.2:340:3-20]386// (3) The effect of the firstprivate clause is as if it is applied to one or387// more leaf constructs as follows:388//  (5) To the distribute construct if it is among the constituent constructs;389//  (6) To the teams construct if it is among the constituent constructs and the390//      distribute construct is not;391//  (8) To a worksharing construct that accepts the clause if one is among the392//      constituent constructs;393//  (9) To the taskloop construct if it is among the constituent constructs;394// (10) To the parallel construct if it is among the constituent constructs and395//      neither a taskloop construct nor a worksharing construct that accepts396//      the clause is among them;397// (12) To the target construct if it is among the constituent constructs and398//      the same list item neither appears in a lastprivate clause nor is the399//      base variable or base pointer of a list item that appears in a map400//      clause.401//402// (15) If the parallel construct is among the constituent constructs and the403// effect is not as if the firstprivate clause is applied to it by the above404// rules, then the effect is as if the shared clause with the same list item is405// applied to the parallel construct.406// (17) If the teams construct is among the constituent constructs and the407// effect is not as if the firstprivate clause is applied to it by the above408// rules, then the effect is as if the shared clause with the same list item is409// applied to the teams construct.410TEST_F(OpenMPDecompositionTest, Firstprivate1) {411  omp::Object x{"x"};412 413  omp::List<omp::Clause> Clauses{414      {OMPC_firstprivate, omp::clause::Firstprivate{{x}}},415  };416 417  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections,418                                  Clauses);419  ASSERT_EQ(Dec.output.size(), 2u);420 421  std::string Dir0 = stringify(Dec.output[0]);422  std::string Dir1 = stringify(Dec.output[1]);423  ASSERT_EQ(Dir0, "parallel shared(x)");       // (10), (15)424  ASSERT_EQ(Dir1, "sections firstprivate(x)"); // (8)425}426 427TEST_F(OpenMPDecompositionTest, Firstprivate2) {428  omp::Object x{"x"};429 430  omp::List<omp::Clause> Clauses{431      {OMPC_firstprivate, omp::clause::Firstprivate{{x}}},432  };433 434  omp::ConstructDecomposition Dec(AnyVersion, Helper,435                                  OMPD_target_teams_distribute, Clauses);436  ASSERT_EQ(Dec.output.size(), 3u);437 438  std::string Dir0 = stringify(Dec.output[0]);439  std::string Dir1 = stringify(Dec.output[1]);440  std::string Dir2 = stringify(Dec.output[2]);441  ASSERT_EQ(Dir0, "target firstprivate(x)");     // (12)442  ASSERT_EQ(Dir1, "teams shared(x)");            // (6), (17)443  ASSERT_EQ(Dir2, "distribute firstprivate(x)"); // (5)444}445 446TEST_F(OpenMPDecompositionTest, Firstprivate3) {447  omp::Object x{"x"};448 449  omp::List<omp::Clause> Clauses{450      {OMPC_firstprivate, omp::clause::Firstprivate{{x}}},451      {OMPC_lastprivate, omp::clause::Lastprivate{{std::nullopt, {x}}}},452  };453 454  omp::ConstructDecomposition Dec(AnyVersion, Helper,455                                  OMPD_target_teams_distribute, Clauses);456  ASSERT_EQ(Dec.output.size(), 3u);457 458  std::string Dir0 = stringify(Dec.output[0]);459  std::string Dir1 = stringify(Dec.output[1]);460  std::string Dir2 = stringify(Dec.output[2]);461  ASSERT_EQ(Dir0, "target map(2, , , , , , (x))"); // (12), (27)462  ASSERT_EQ(Dir1, "teams shared(x)");              // (6), (17)463  ASSERT_EQ(Dir2, "distribute firstprivate(x) lastprivate(, (x))"); // (5), (21)464}465 466TEST_F(OpenMPDecompositionTest, Firstprivate4) {467  omp::Object x{"x"};468 469  omp::List<omp::Clause> Clauses{470      {OMPC_firstprivate, omp::clause::Firstprivate{{x}}},471  };472 473  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_target_teams,474                                  Clauses);475  ASSERT_EQ(Dec.output.size(), 2u);476 477  std::string Dir0 = stringify(Dec.output[0]);478  std::string Dir1 = stringify(Dec.output[1]);479  ASSERT_EQ(Dir0, "target firstprivate(x)"); // (12)480  ASSERT_EQ(Dir1, "teams firstprivate(x)");  // (6)481}482 483TEST_F(OpenMPDecompositionTest, Firstprivate5) {484  omp::Object x{"x"};485 486  omp::List<omp::Clause> Clauses{487      {OMPC_firstprivate, omp::clause::Firstprivate{{x}}},488  };489 490  omp::ConstructDecomposition Dec(AnyVersion, Helper,491                                  OMPD_parallel_masked_taskloop, Clauses);492  ASSERT_EQ(Dec.output.size(), 3u);493 494  std::string Dir0 = stringify(Dec.output[0]);495  std::string Dir1 = stringify(Dec.output[1]);496  std::string Dir2 = stringify(Dec.output[2]);497  ASSERT_EQ(Dir0, "parallel shared(x)"); // (10)498  ASSERT_EQ(Dir1, "masked");499  ASSERT_EQ(Dir2, "taskloop firstprivate(x)"); // (9)500}501 502TEST_F(OpenMPDecompositionTest, Firstprivate6) {503  omp::Object x{"x"};504 505  omp::List<omp::Clause> Clauses{506      {OMPC_firstprivate, omp::clause::Firstprivate{{x}}},507  };508 509  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_masked,510                                  Clauses);511  ASSERT_EQ(Dec.output.size(), 2u);512 513  std::string Dir0 = stringify(Dec.output[0]);514  std::string Dir1 = stringify(Dec.output[1]);515  ASSERT_EQ(Dir0, "parallel firstprivate(x)"); // (10)516  ASSERT_EQ(Dir1, "masked");517}518 519TEST_F(OpenMPDecompositionTest, Firstprivate7) {520  omp::Object x{"x"};521 522  omp::List<omp::Clause> Clauses{523      {OMPC_firstprivate, omp::clause::Firstprivate{{x}}},524  };525 526  // Composite constructs are still decomposed.527  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_teams_distribute,528                                  Clauses);529  ASSERT_EQ(Dec.output.size(), 2u);530 531  std::string Dir0 = stringify(Dec.output[0]);532  std::string Dir1 = stringify(Dec.output[1]);533  ASSERT_EQ(Dir0, "teams shared(x)");            // (17)534  ASSERT_EQ(Dir1, "distribute firstprivate(x)"); // (5)535}536 537// LASTPRIVATE538// [5.2:115:7-8]539// Directives: distribute, do, for, loop, sections, simd, taskloop540//541// [5.2:340:21-30]542// (21) The effect of the lastprivate clause is as if it is applied to all leaf543// constructs that permit the clause.544// (22) If the parallel construct is among the constituent constructs and the545// list item is not also specified in the firstprivate clause, then the effect546// of the lastprivate clause is as if the shared clause with the same list item547// is applied to the parallel construct.548// (24) If the teams construct is among the constituent constructs and the list549// item is not also specified in the firstprivate clause, then the effect of the550// lastprivate clause is as if the shared clause with the same list item is551// applied to the teams construct.552// (27) If the target construct is among the constituent constructs and the list553// item is not the base variable or base pointer of a list item that appears in554// a map clause, the effect of the lastprivate clause is as if the same list555// item appears in a map clause with a map-type of tofrom.556TEST_F(OpenMPDecompositionTest, Lastprivate1) {557  omp::Object x{"x"};558 559  omp::List<omp::Clause> Clauses{560      {OMPC_lastprivate, omp::clause::Lastprivate{{std::nullopt, {x}}}},561  };562 563  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections,564                                  Clauses);565  ASSERT_EQ(Dec.output.size(), 2u);566 567  std::string Dir0 = stringify(Dec.output[0]);568  std::string Dir1 = stringify(Dec.output[1]);569  ASSERT_EQ(Dir0, "parallel shared(x)");          // (21), (22)570  ASSERT_EQ(Dir1, "sections lastprivate(, (x))"); // (21)571}572 573TEST_F(OpenMPDecompositionTest, Lastprivate2) {574  omp::Object x{"x"};575 576  omp::List<omp::Clause> Clauses{577      {OMPC_lastprivate, omp::clause::Lastprivate{{std::nullopt, {x}}}},578  };579 580  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_teams_distribute,581                                  Clauses);582  ASSERT_EQ(Dec.output.size(), 2u);583 584  std::string Dir0 = stringify(Dec.output[0]);585  std::string Dir1 = stringify(Dec.output[1]);586  ASSERT_EQ(Dir0, "teams shared(x)");               // (21), (25)587  ASSERT_EQ(Dir1, "distribute lastprivate(, (x))"); // (21)588}589 590TEST_F(OpenMPDecompositionTest, Lastprivate3) {591  omp::Object x{"x"};592 593  omp::List<omp::Clause> Clauses{594      {OMPC_lastprivate, omp::clause::Lastprivate{{std::nullopt, {x}}}},595  };596 597  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_target_parallel_do,598                                  Clauses);599  ASSERT_EQ(Dec.output.size(), 3u);600 601  std::string Dir0 = stringify(Dec.output[0]);602  std::string Dir1 = stringify(Dec.output[1]);603  std::string Dir2 = stringify(Dec.output[2]);604  ASSERT_EQ(Dir0, "target map(2, , , , , , (x))"); // (21), (27)605  ASSERT_EQ(Dir1, "parallel shared(x)");           // (22)606  ASSERT_EQ(Dir2, "do lastprivate(, (x))");        // (21)607}608 609// SHARED610// [5.2:110:5-6]611// Directives: parallel, task, taskloop, teams612//613// [5.2:340:31-32]614// (31) The effect of the shared, default, thread_limit, or order clause is as615// if it is applied to all leaf constructs that permit the clause.616TEST_F(OpenMPDecompositionTest, Shared1) {617  omp::Object x{"x"};618 619  omp::List<omp::Clause> Clauses{620      {OMPC_shared, omp::clause::Shared{{x}}},621  };622 623  omp::ConstructDecomposition Dec(AnyVersion, Helper,624                                  OMPD_parallel_masked_taskloop, Clauses);625  ASSERT_EQ(Dec.output.size(), 3u);626 627  std::string Dir0 = stringify(Dec.output[0]);628  std::string Dir1 = stringify(Dec.output[1]);629  std::string Dir2 = stringify(Dec.output[2]);630  ASSERT_EQ(Dir0, "parallel shared(x)"); // (31)631  ASSERT_EQ(Dir1, "masked");             // (31)632  ASSERT_EQ(Dir2, "taskloop shared(x)"); // (31)633}634 635// DEFAULT636// [5.2:109:5-6]637// Directives: parallel, task, taskloop, teams638//639// [5.2:340:31-32]640// (31) The effect of the shared, default, thread_limit, or order clause is as641// if it is applied to all leaf constructs that permit the clause.642TEST_F(OpenMPDecompositionTest, Default1) {643  omp::Object x{"x"};644 645  omp::List<omp::Clause> Clauses{646      {OMPC_default,647       omp::clause::Default{648           omp::clause::Default::DataSharingAttribute::Firstprivate}},649  };650 651  omp::ConstructDecomposition Dec(AnyVersion, Helper,652                                  OMPD_parallel_masked_taskloop, Clauses);653  ASSERT_EQ(Dec.output.size(), 3u);654 655  std::string Dir0 = stringify(Dec.output[0]);656  std::string Dir1 = stringify(Dec.output[1]);657  std::string Dir2 = stringify(Dec.output[2]);658  ASSERT_EQ(Dir0, "parallel default(0)"); // (31)659  ASSERT_EQ(Dir1, "masked");              // (31)660  ASSERT_EQ(Dir2, "taskloop default(0)"); // (31)661}662 663// THREAD_LIMIT664// [5.2:277:14-15]665// Directives: target, teams666//667// [5.2:340:31-32]668// (31) The effect of the shared, default, thread_limit, or order clause is as669// if it is applied to all leaf constructs that permit the clause.670TEST_F(OpenMPDecompositionTest, ThreadLimit1) {671  omp::Object x{"x"};672 673  omp::List<omp::Clause> Clauses{674      {OMPC_thread_limit, omp::clause::ThreadLimit{omp::ExprTy{}}},675  };676 677  omp::ConstructDecomposition Dec(AnyVersion, Helper,678                                  OMPD_target_teams_distribute, Clauses);679  ASSERT_EQ(Dec.output.size(), 3u);680 681  std::string Dir0 = stringify(Dec.output[0]);682  std::string Dir1 = stringify(Dec.output[1]);683  std::string Dir2 = stringify(Dec.output[2]);684  ASSERT_EQ(Dir0, "target thread_limit(expr)"); // (31)685  ASSERT_EQ(Dir1, "teams thread_limit(expr)");  // (31)686  ASSERT_EQ(Dir2, "distribute");                // (31)687}688 689// ORDER690// [5.2:234:3-4]691// Directives: distribute, do, for, loop, simd692//693// [5.2:340:31-32]694// (31) The effect of the shared, default, thread_limit, or order clause is as695// if it is applied to all leaf constructs that permit the clause.696TEST_F(OpenMPDecompositionTest, Order1) {697  omp::Object x{"x"};698 699  omp::List<omp::Clause> Clauses{700      {OMPC_order,701       omp::clause::Order{{omp::clause::Order::OrderModifier::Unconstrained,702                           omp::clause::Order::Ordering::Concurrent}}},703  };704 705  omp::ConstructDecomposition Dec(706      AnyVersion, Helper, OMPD_target_teams_distribute_parallel_for_simd,707      Clauses);708  ASSERT_EQ(Dec.output.size(), 6u);709 710  std::string Dir0 = stringify(Dec.output[0]);711  std::string Dir1 = stringify(Dec.output[1]);712  std::string Dir2 = stringify(Dec.output[2]);713  std::string Dir3 = stringify(Dec.output[3]);714  std::string Dir4 = stringify(Dec.output[4]);715  std::string Dir5 = stringify(Dec.output[5]);716  ASSERT_EQ(Dir0, "target");                 // (31)717  ASSERT_EQ(Dir1, "teams");                  // (31)718  ASSERT_EQ(Dir2, "distribute order(1, 0)"); // (31)719  ASSERT_EQ(Dir3, "parallel");               // (31)720  ASSERT_EQ(Dir4, "for order(1, 0)");        // (31)721  ASSERT_EQ(Dir5, "simd order(1, 0)");       // (31)722}723 724// ALLOCATE725// [5.2:178:7-9]726// Directives: allocators, distribute, do, for, parallel, scope, sections,727// single, target, task, taskgroup, taskloop, teams728//729// [5.2:340:33-35]730// (33) The effect of the allocate clause is as if it is applied to all leaf731// constructs that permit the clause and to which a data-sharing attribute732// clause that may create a private copy of the same list item is applied.733TEST_F(OpenMPDecompositionTest, Allocate1) {734  omp::Object x{"x"};735 736  // Allocate + firstprivate737  omp::List<omp::Clause> Clauses{738      {OMPC_allocate, omp::clause::Allocate{{std::nullopt, std::nullopt, {x}}}},739      {OMPC_firstprivate, omp::clause::Firstprivate{{x}}},740  };741 742  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections,743                                  Clauses);744  ASSERT_EQ(Dec.output.size(), 2u);745 746  std::string Dir0 = stringify(Dec.output[0]);747  std::string Dir1 = stringify(Dec.output[1]);748  ASSERT_EQ(Dir0, "parallel shared(x)");                         // (33)749  ASSERT_EQ(Dir1, "sections firstprivate(x) allocate(, , (x))"); // (33)750}751 752TEST_F(OpenMPDecompositionTest, Allocate2) {753  omp::Object x{"x"};754  auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add);755 756  // Allocate + in_reduction757  omp::List<omp::Clause> Clauses{758      {OMPC_allocate, omp::clause::Allocate{{std::nullopt, std::nullopt, {x}}}},759      {OMPC_in_reduction, omp::clause::InReduction{{{Add}, {x}}}},760  };761 762  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_target_parallel,763                                  Clauses);764  ASSERT_EQ(Dec.output.size(), 2u);765 766  std::string Dir0 = stringify(Dec.output[0]);767  std::string Dir1 = stringify(Dec.output[1]);768  ASSERT_EQ(Dir0, "target in_reduction((3), (x)) allocate(, , (x))"); // (33)769  ASSERT_EQ(Dir1, "parallel");                                        // (33)770}771 772TEST_F(OpenMPDecompositionTest, Allocate3) {773  omp::Object x{"x"};774 775  // Allocate + linear776  omp::List<omp::Clause> Clauses{777      {OMPC_allocate, omp::clause::Allocate{{std::nullopt, std::nullopt, {x}}}},778      {OMPC_linear, omp::clause::Linear{{std::nullopt, std::nullopt, {x}}}},779  };780 781  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_for,782                                  Clauses);783  ASSERT_EQ(Dec.output.size(), 2u);784 785  std::string Dir0 = stringify(Dec.output[0]);786  std::string Dir1 = stringify(Dec.output[1]);787  // The "shared" clause is duplicated---this isn't harmful, but it788  // should be fixed eventually.789  ASSERT_EQ(Dir0, "parallel shared(x) shared(x)"); // (33)790  ASSERT_EQ(Dir1, "for linear(, , (x)) firstprivate(x) lastprivate(, (x)) "791                  "allocate(, , (x))"); // (33)792}793 794TEST_F(OpenMPDecompositionTest, Allocate4) {795  omp::Object x{"x"};796 797  // Allocate + lastprivate798  omp::List<omp::Clause> Clauses{799      {OMPC_allocate, omp::clause::Allocate{{std::nullopt, std::nullopt, {x}}}},800      {OMPC_lastprivate, omp::clause::Lastprivate{{std::nullopt, {x}}}},801  };802 803  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections,804                                  Clauses);805  ASSERT_EQ(Dec.output.size(), 2u);806 807  std::string Dir0 = stringify(Dec.output[0]);808  std::string Dir1 = stringify(Dec.output[1]);809  ASSERT_EQ(Dir0, "parallel shared(x)");                            // (33)810  ASSERT_EQ(Dir1, "sections lastprivate(, (x)) allocate(, , (x))"); // (33)811}812 813TEST_F(OpenMPDecompositionTest, Allocate5) {814  omp::Object x{"x"};815 816  // Allocate + private817  omp::List<omp::Clause> Clauses{818      {OMPC_allocate, omp::clause::Allocate{{std::nullopt, std::nullopt, {x}}}},819      {OMPC_private, omp::clause::Private{{x}}},820  };821 822  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections,823                                  Clauses);824  ASSERT_EQ(Dec.output.size(), 2u);825 826  std::string Dir0 = stringify(Dec.output[0]);827  std::string Dir1 = stringify(Dec.output[1]);828  ASSERT_EQ(Dir0, "parallel");                              // (33)829  ASSERT_EQ(Dir1, "sections private(x) allocate(, , (x))"); // (33)830}831 832TEST_F(OpenMPDecompositionTest, Allocate6) {833  omp::Object x{"x"};834  auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add);835 836  // Allocate + reduction837  omp::List<omp::Clause> Clauses{838      {OMPC_allocate, omp::clause::Allocate{{std::nullopt, std::nullopt, {x}}}},839      {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}},840  };841 842  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections,843                                  Clauses);844  ASSERT_EQ(Dec.output.size(), 2u);845 846  std::string Dir0 = stringify(Dec.output[0]);847  std::string Dir1 = stringify(Dec.output[1]);848  ASSERT_EQ(Dir0, "parallel shared(x)");                               // (33)849  ASSERT_EQ(Dir1, "sections reduction(, (3), (x)) allocate(, , (x))"); // (33)850}851 852// REDUCTION853// [5.2:134:17-18]854// Directives: do, for, loop, parallel, scope, sections, simd, taskloop, teams855//856// [5.2:340-341:36-13]857// (36) The effect of the reduction clause is as if it is applied to all leaf858// constructs that permit the clause, except for the following constructs:859//  (1) The parallel construct, when combined with the sections,860//      worksharing-loop, loop, or taskloop construct; and861//  (3) The teams construct, when combined with the loop construct.862// (4) For the parallel and teams constructs above, the effect of the reduction863// clause instead is as if each list item or, for any list item that is an array864// item, its corresponding base array or base pointer appears in a shared clause865// for the construct.866// (6) If the task reduction-modifier is specified, the effect is as if it only867// modifies the behavior of the reduction clause on the innermost leaf construct868// that accepts the modifier (see Section 5.5.8).869// (8) If the inscan reduction-modifier is specified, the effect is as if it870// modifies the behavior of the reduction clause on all constructs of the871// combined construct to which the clause is applied and that accept the872// modifier.873// (10) If a list item in a reduction clause on a combined target construct does874// not have the same base variable or base pointer as a list item in a map875// clause on the construct, then the effect is as if the list item in the876// reduction clause appears as a list item in a map clause with a map-type of877// tofrom.878TEST_F(OpenMPDecompositionTest, Reduction1) {879  omp::Object x{"x"};880  auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add);881 882  omp::List<omp::Clause> Clauses{883      {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}},884  };885 886  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections,887                                  Clauses);888  ASSERT_EQ(Dec.output.size(), 2u);889 890  std::string Dir0 = stringify(Dec.output[0]);891  std::string Dir1 = stringify(Dec.output[1]);892  ASSERT_EQ(Dir0, "parallel shared(x)");             // (36), (1), (4)893  ASSERT_EQ(Dir1, "sections reduction(, (3), (x))"); // (36)894}895 896TEST_F(OpenMPDecompositionTest, Reduction2) {897  omp::Object x{"x"};898  auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add);899 900  omp::List<omp::Clause> Clauses{901      {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}},902  };903 904  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_masked,905                                  Clauses);906  ASSERT_EQ(Dec.output.size(), 2u);907 908  std::string Dir0 = stringify(Dec.output[0]);909  std::string Dir1 = stringify(Dec.output[1]);910  ASSERT_EQ(Dir0, "parallel reduction(, (3), (x))"); // (36), (1), (4)911  ASSERT_EQ(Dir1, "masked");                         // (36)912}913 914TEST_F(OpenMPDecompositionTest, Reduction3) {915  omp::Object x{"x"};916  auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add);917 918  omp::List<omp::Clause> Clauses{919      {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}},920  };921 922  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_teams_loop, Clauses);923  ASSERT_EQ(Dec.output.size(), 2u);924 925  std::string Dir0 = stringify(Dec.output[0]);926  std::string Dir1 = stringify(Dec.output[1]);927  ASSERT_EQ(Dir0, "teams shared(x)");            // (36), (3), (4)928  ASSERT_EQ(Dir1, "loop reduction(, (3), (x))"); // (36)929}930 931TEST_F(OpenMPDecompositionTest, Reduction4) {932  omp::Object x{"x"};933  auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add);934 935  omp::List<omp::Clause> Clauses{936      {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}},937  };938 939  omp::ConstructDecomposition Dec(AnyVersion, Helper,940                                  OMPD_teams_distribute_parallel_for, Clauses);941  ASSERT_EQ(Dec.output.size(), 4u);942 943  std::string Dir0 = stringify(Dec.output[0]);944  std::string Dir1 = stringify(Dec.output[1]);945  std::string Dir2 = stringify(Dec.output[2]);946  std::string Dir3 = stringify(Dec.output[3]);947  ASSERT_EQ(Dir0, "teams reduction(, (3), (x))"); // (36), (3)948  ASSERT_EQ(Dir1, "distribute");                  // (36)949  ASSERT_EQ(Dir2, "parallel shared(x)");          // (36), (1), (4)950  ASSERT_EQ(Dir3, "for reduction(, (3), (x))");   // (36)951}952 953TEST_F(OpenMPDecompositionTest, Reduction5) {954  omp::Object x{"x"};955  auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add);956  auto TaskMod = omp::clause::Reduction::ReductionModifier::Task;957 958  omp::List<omp::Clause> Clauses{959      {OMPC_reduction, omp::clause::Reduction{{TaskMod, {Add}, {x}}}},960  };961 962  omp::ConstructDecomposition Dec(AnyVersion, Helper,963                                  OMPD_teams_distribute_parallel_for, Clauses);964  ASSERT_EQ(Dec.output.size(), 4u);965 966  std::string Dir0 = stringify(Dec.output[0]);967  std::string Dir1 = stringify(Dec.output[1]);968  std::string Dir2 = stringify(Dec.output[2]);969  std::string Dir3 = stringify(Dec.output[3]);970  ASSERT_EQ(Dir0, "teams reduction(, (3), (x))"); // (36), (3), (6)971  ASSERT_EQ(Dir1, "distribute");                  // (36)972  ASSERT_EQ(Dir2, "parallel shared(x)");          // (36), (1), (4)973  ASSERT_EQ(Dir3, "for reduction(2, (3), (x))");  // (36), (6)974}975 976TEST_F(OpenMPDecompositionTest, Reduction6) {977  omp::Object x{"x"};978  auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add);979  auto InscanMod = omp::clause::Reduction::ReductionModifier::Inscan;980 981  omp::List<omp::Clause> Clauses{982      {OMPC_reduction, omp::clause::Reduction{{InscanMod, {Add}, {x}}}},983  };984 985  omp::ConstructDecomposition Dec(AnyVersion, Helper,986                                  OMPD_teams_distribute_parallel_for, Clauses);987  ASSERT_EQ(Dec.output.size(), 4u);988 989  std::string Dir0 = stringify(Dec.output[0]);990  std::string Dir1 = stringify(Dec.output[1]);991  std::string Dir2 = stringify(Dec.output[2]);992  std::string Dir3 = stringify(Dec.output[3]);993  ASSERT_EQ(Dir0, "teams reduction(, (3), (x))"); // (36), (3), (8)994  ASSERT_EQ(Dir1, "distribute");                  // (36)995  ASSERT_EQ(Dir2, "parallel shared(x)");          // (36), (1), (4)996  ASSERT_EQ(Dir3, "for reduction(1, (3), (x))");  // (36), (8)997}998 999TEST_F(OpenMPDecompositionTest, Reduction7) {1000  omp::Object x{"x"};1001  auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add);1002 1003  omp::List<omp::Clause> Clauses{1004      {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}},1005  };1006 1007  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_target_parallel_do,1008                                  Clauses);1009  ASSERT_EQ(Dec.output.size(), 3u);1010 1011  std::string Dir0 = stringify(Dec.output[0]);1012  std::string Dir1 = stringify(Dec.output[1]);1013  std::string Dir2 = stringify(Dec.output[2]);1014  ASSERT_EQ(Dir0, "target map(2, , , , , , (x))"); // (36), (10)1015  ASSERT_EQ(Dir1, "parallel shared(x)");           // (36), (1), (4)1016  ASSERT_EQ(Dir2, "do reduction(, (3), (x))");     // (36)1017}1018 1019// IF1020// [5.2:72:7-9]1021// Directives: cancel, parallel, simd, target, target data, target enter data,1022// target exit data, target update, task, taskloop1023//1024// [5.2:72:15-18]1025// (15) For combined or composite constructs, the if clause only applies to the1026// semantics of the construct named in the directive-name-modifier.1027// (16) For a combined or composite construct, if no directive-name-modifier is1028// specified then the if clause applies to all constituent constructs to which1029// an if clause can apply.1030TEST_F(OpenMPDecompositionTest, If1) {1031  omp::List<omp::Clause> Clauses{1032      {OMPC_if,1033       omp::clause::If{{llvm::omp::Directive::OMPD_parallel, omp::ExprTy{}}}},1034  };1035 1036  omp::ConstructDecomposition Dec(AnyVersion, Helper,1037                                  OMPD_target_parallel_for_simd, Clauses);1038  ASSERT_EQ(Dec.output.size(), 4u);1039  std::string Dir0 = stringify(Dec.output[0]);1040  std::string Dir1 = stringify(Dec.output[1]);1041  std::string Dir2 = stringify(Dec.output[2]);1042  std::string Dir3 = stringify(Dec.output[3]);1043  ASSERT_EQ(Dir0, "target");              // (15)1044  ASSERT_EQ(Dir1, "parallel if(, expr)"); // (15)1045  ASSERT_EQ(Dir2, "for");                 // (15)1046  ASSERT_EQ(Dir3, "simd");                // (15)1047}1048 1049TEST_F(OpenMPDecompositionTest, If2) {1050  omp::List<omp::Clause> Clauses{1051      {OMPC_if, omp::clause::If{{std::nullopt, omp::ExprTy{}}}},1052  };1053 1054  omp::ConstructDecomposition Dec(AnyVersion, Helper,1055                                  OMPD_target_parallel_for_simd, Clauses);1056  ASSERT_EQ(Dec.output.size(), 4u);1057  std::string Dir0 = stringify(Dec.output[0]);1058  std::string Dir1 = stringify(Dec.output[1]);1059  std::string Dir2 = stringify(Dec.output[2]);1060  std::string Dir3 = stringify(Dec.output[3]);1061  ASSERT_EQ(Dir0, "target if(, expr)");   // (16)1062  ASSERT_EQ(Dir1, "parallel if(, expr)"); // (16)1063  ASSERT_EQ(Dir2, "for");                 // (16)1064  ASSERT_EQ(Dir3, "simd if(, expr)");     // (16)1065}1066 1067// LINEAR1068// [5.2:118:1-2]1069// Directives: declare simd, do, for, simd1070//1071// [5.2:341:15-22]1072// (15.1) The effect of the linear clause is as if it is applied to the1073// innermost leaf construct.1074// (15.2) Additionally, if the list item is not the iteration variable of a simd1075// or worksharing-loop SIMD construct, the effect on the outer leaf constructs1076// is as if the list item was specified in firstprivate and lastprivate clauses1077// on the combined or composite construct, with the rules specified above1078// applied.1079// (19) If a list item of the linear clause is the iteration variable of a simd1080// or worksharing-loop SIMD construct and it is not declared in the construct,1081// the effect on the outer leaf constructs is as if the list item was specified1082// in a lastprivate clause on the combined or composite construct with the rules1083// specified above applied.1084TEST_F(OpenMPDecompositionTest, Linear1) {1085  omp::Object x{"x"};1086 1087  omp::List<omp::Clause> Clauses{1088      {OMPC_linear, omp::clause::Linear{{std::nullopt, std::nullopt, {x}}}},1089  };1090 1091  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_for_simd, Clauses);1092  ASSERT_EQ(Dec.output.size(), 2u);1093  std::string Dir0 = stringify(Dec.output[0]);1094  std::string Dir1 = stringify(Dec.output[1]);1095  ASSERT_EQ(Dir0, "for firstprivate(x) lastprivate(, (x))"); // (15.1), (15.2)1096  ASSERT_EQ(Dir1, "simd linear(, , (x)) lastprivate(, (x))"); // (15.1)1097}1098 1099// NOWAIT1100// [5.2:308:11-13]1101// Directives: dispatch, do, for, interop, scope, sections, single, target,1102// target enter data, target exit data, target update, taskwait, workshare1103//1104// [5.2:341:23]1105// (23) The effect of the nowait clause is as if it is applied to the outermost1106// leaf construct that permits it.1107TEST_F(OpenMPDecompositionTest, Nowait1) {1108  omp::List<omp::Clause> Clauses{1109      {OMPC_nowait, omp::clause::Nowait{}},1110  };1111 1112  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_target_parallel_for,1113                                  Clauses);1114  ASSERT_EQ(Dec.output.size(), 3u);1115  std::string Dir0 = stringify(Dec.output[0]);1116  std::string Dir1 = stringify(Dec.output[1]);1117  std::string Dir2 = stringify(Dec.output[2]);1118  ASSERT_EQ(Dir0, "target nowait"); // (23)1119  ASSERT_EQ(Dir1, "parallel");      // (23)1120  ASSERT_EQ(Dir2, "for");           // (23)1121}1122 1123// ---1124 1125// Check that "simd linear(x)" does not fail despite the implied "firstprivate"1126// (which "simd" does not allow).1127TEST_F(OpenMPDecompositionTest, Misc1) {1128  omp::Object x{"x"};1129  omp::List<omp::Clause> Clauses{1130      {OMPC_linear, omp::clause::Linear{{std::nullopt, std::nullopt, {x}}}},1131  };1132 1133  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_simd, Clauses);1134  ASSERT_EQ(Dec.output.size(), 1u);1135  std::string Dir0 = stringify(Dec.output[0]);1136  ASSERT_EQ(Dir0, "simd linear(, , (x)) lastprivate(, (x))");1137}1138 1139// --- Failure/error reporting tests1140 1141TEST_F(OpenMPDecompositionTest, Error1) {1142  // "parallel for at(compilation)" is invalid because the "at" clause1143  // does not apply to either "parallel" or "for".1144 1145  omp::List<omp::Clause> Clauses{1146      {OMPC_at, omp::clause::At{omp::clause::At::ActionTime::Compilation}},1147  };1148 1149  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_for,1150                                  Clauses);1151  ASSERT_EQ(Dec.errors.size(), 1u);1152  std::string Err0 = stringify(Dec.errors[0]);1153  ASSERT_EQ(Err0,1154            "error while applying 'at(0)': no leaf that allows this clause");1155}1156 1157TEST_F(OpenMPDecompositionTest, Error2) {1158  // "parallel loop allocate(x) private(x)" is invalid because "allocate"1159  // can only be applied to "parallel", while "private" is applied to "loop".1160  // This violates the requirement that the leaf with an "allocate" also has1161  // a privatizing clause.1162 1163  omp::Object x{"x"};1164 1165  omp::List<omp::Clause> Clauses{1166      {OMPC_allocate, omp::clause::Allocate{{std::nullopt, std::nullopt, {x}}}},1167      {OMPC_private, omp::clause::Private{{x}}},1168  };1169 1170  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_loop,1171                                  Clauses);1172  ASSERT_EQ(Dec.errors.size(), 1u);1173  std::string Err0 = stringify(Dec.errors[0]);1174  ASSERT_EQ(Err0, "error while applying 'allocate(, , (x))': no leaf with a "1175                  "privatizing clause");1176}1177 1178TEST_F(OpenMPDecompositionTest, Error3) {1179  // "parallel for if(target: e)" is invalid because the "target" directive-1180  // name-modifier does not refer to a constituent directive.1181 1182  omp::ExprTy e;1183 1184  omp::List<omp::Clause> Clauses{1185      {OMPC_if, omp::clause::If{{llvm::omp::Directive::OMPD_target, e}}},1186  };1187 1188  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_for,1189                                  Clauses);1190  ASSERT_EQ(Dec.errors.size(), 1u);1191  std::string Err0 = stringify(Dec.errors[0]);1192  ASSERT_EQ(Err0, "error while applying 'if(target, expr)': invalid directive "1193                  "name modifier");1194}1195 1196TEST_F(OpenMPDecompositionTest, Error4) {1197  // "masked taskloop reduction(+, task: x)" is invalid because the "task"1198  // modifier can only be applied to "parallel" or a worksharing directive.1199 1200  omp::Object x{"x"};1201  auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add);1202  auto TaskMod = omp::clause::Reduction::ReductionModifier::Task;1203 1204  omp::List<omp::Clause> Clauses{1205      {OMPC_reduction, omp::clause::Reduction{{TaskMod, {Add}, {x}}}},1206  };1207 1208  omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_masked_taskloop,1209                                  Clauses);1210  ASSERT_EQ(Dec.errors.size(), 1u);1211  std::string Err0 = stringify(Dec.errors[0]);1212  ASSERT_EQ(Err0, "error while applying 'reduction(2, (3), (x))': the "1213                  "reduction modifier cannot be applied");1214}1215} // namespace1216