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