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-exception 6 // 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 construction 28 // of clauses, and implement emitting of a directive with clauses to a string. 29 // 30 // The tests then follow the pattern 31 // 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 applied 34 // to them. 35 // 4. Convert them to strings and compare with expected outputs. 36 37 namespace omp { 38 struct TypeTy {}; // placeholder 39 struct ExprTy {}; // placeholder 40 using IdTy = std::string; 41 } // namespace omp 42 43 namespace tomp::type { 44 template <> 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::type 51 52 namespace omp { 53 template <typename ElemTy> using List = tomp::type::ListT<ElemTy>; 54 55 using Object = tomp::ObjectT<IdTy, ExprTy>; 56 57 namespace clause { 58 using DefinedOperator = tomp::type::DefinedOperatorT<IdTy, ExprTy>; 59 using ProcedureDesignator = tomp::type::ProcedureDesignatorT<IdTy, ExprTy>; 60 using ReductionOperator = tomp::type::ReductionIdentifierT<IdTy, ExprTy>; 61 62 using AcqRel = tomp::clause::AcqRelT<TypeTy, IdTy, ExprTy>; 63 using Acquire = tomp::clause::AcquireT<TypeTy, IdTy, ExprTy>; 64 using AdjustArgs = tomp::clause::AdjustArgsT<TypeTy, IdTy, ExprTy>; 65 using Affinity = tomp::clause::AffinityT<TypeTy, IdTy, ExprTy>; 66 using Aligned = tomp::clause::AlignedT<TypeTy, IdTy, ExprTy>; 67 using Align = tomp::clause::AlignT<TypeTy, IdTy, ExprTy>; 68 using Allocate = tomp::clause::AllocateT<TypeTy, IdTy, ExprTy>; 69 using Allocator = tomp::clause::AllocatorT<TypeTy, IdTy, ExprTy>; 70 using AppendArgs = tomp::clause::AppendArgsT<TypeTy, IdTy, ExprTy>; 71 using AtomicDefaultMemOrder = 72 tomp::clause::AtomicDefaultMemOrderT<TypeTy, IdTy, ExprTy>; 73 using At = tomp::clause::AtT<TypeTy, IdTy, ExprTy>; 74 using Bind = tomp::clause::BindT<TypeTy, IdTy, ExprTy>; 75 using Capture = tomp::clause::CaptureT<TypeTy, IdTy, ExprTy>; 76 using Collapse = tomp::clause::CollapseT<TypeTy, IdTy, ExprTy>; 77 using Compare = tomp::clause::CompareT<TypeTy, IdTy, ExprTy>; 78 using Copyin = tomp::clause::CopyinT<TypeTy, IdTy, ExprTy>; 79 using Copyprivate = tomp::clause::CopyprivateT<TypeTy, IdTy, ExprTy>; 80 using Defaultmap = tomp::clause::DefaultmapT<TypeTy, IdTy, ExprTy>; 81 using Default = tomp::clause::DefaultT<TypeTy, IdTy, ExprTy>; 82 using Depend = tomp::clause::DependT<TypeTy, IdTy, ExprTy>; 83 using Destroy = tomp::clause::DestroyT<TypeTy, IdTy, ExprTy>; 84 using Detach = tomp::clause::DetachT<TypeTy, IdTy, ExprTy>; 85 using Device = tomp::clause::DeviceT<TypeTy, IdTy, ExprTy>; 86 using DeviceType = tomp::clause::DeviceTypeT<TypeTy, IdTy, ExprTy>; 87 using DistSchedule = tomp::clause::DistScheduleT<TypeTy, IdTy, ExprTy>; 88 using Doacross = tomp::clause::DoacrossT<TypeTy, IdTy, ExprTy>; 89 using DynamicAllocators = 90 tomp::clause::DynamicAllocatorsT<TypeTy, IdTy, ExprTy>; 91 using Enter = tomp::clause::EnterT<TypeTy, IdTy, ExprTy>; 92 using Exclusive = tomp::clause::ExclusiveT<TypeTy, IdTy, ExprTy>; 93 using Fail = tomp::clause::FailT<TypeTy, IdTy, ExprTy>; 94 using Filter = tomp::clause::FilterT<TypeTy, IdTy, ExprTy>; 95 using Final = tomp::clause::FinalT<TypeTy, IdTy, ExprTy>; 96 using Firstprivate = tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>; 97 using From = tomp::clause::FromT<TypeTy, IdTy, ExprTy>; 98 using Full = tomp::clause::FullT<TypeTy, IdTy, ExprTy>; 99 using Grainsize = tomp::clause::GrainsizeT<TypeTy, IdTy, ExprTy>; 100 using HasDeviceAddr = tomp::clause::HasDeviceAddrT<TypeTy, IdTy, ExprTy>; 101 using Hint = tomp::clause::HintT<TypeTy, IdTy, ExprTy>; 102 using If = tomp::clause::IfT<TypeTy, IdTy, ExprTy>; 103 using Inbranch = tomp::clause::InbranchT<TypeTy, IdTy, ExprTy>; 104 using Inclusive = tomp::clause::InclusiveT<TypeTy, IdTy, ExprTy>; 105 using Indirect = tomp::clause::IndirectT<TypeTy, IdTy, ExprTy>; 106 using Init = tomp::clause::InitT<TypeTy, IdTy, ExprTy>; 107 using InReduction = tomp::clause::InReductionT<TypeTy, IdTy, ExprTy>; 108 using IsDevicePtr = tomp::clause::IsDevicePtrT<TypeTy, IdTy, ExprTy>; 109 using Lastprivate = tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy>; 110 using Linear = tomp::clause::LinearT<TypeTy, IdTy, ExprTy>; 111 using Link = tomp::clause::LinkT<TypeTy, IdTy, ExprTy>; 112 using Map = tomp::clause::MapT<TypeTy, IdTy, ExprTy>; 113 using Match = tomp::clause::MatchT<TypeTy, IdTy, ExprTy>; 114 using Mergeable = tomp::clause::MergeableT<TypeTy, IdTy, ExprTy>; 115 using Message = tomp::clause::MessageT<TypeTy, IdTy, ExprTy>; 116 using Nocontext = tomp::clause::NocontextT<TypeTy, IdTy, ExprTy>; 117 using Nogroup = tomp::clause::NogroupT<TypeTy, IdTy, ExprTy>; 118 using Nontemporal = tomp::clause::NontemporalT<TypeTy, IdTy, ExprTy>; 119 using Notinbranch = tomp::clause::NotinbranchT<TypeTy, IdTy, ExprTy>; 120 using Novariants = tomp::clause::NovariantsT<TypeTy, IdTy, ExprTy>; 121 using Nowait = tomp::clause::NowaitT<TypeTy, IdTy, ExprTy>; 122 using NumTasks = tomp::clause::NumTasksT<TypeTy, IdTy, ExprTy>; 123 using NumTeams = tomp::clause::NumTeamsT<TypeTy, IdTy, ExprTy>; 124 using NumThreads = tomp::clause::NumThreadsT<TypeTy, IdTy, ExprTy>; 125 using OmpxAttribute = tomp::clause::OmpxAttributeT<TypeTy, IdTy, ExprTy>; 126 using OmpxBare = tomp::clause::OmpxBareT<TypeTy, IdTy, ExprTy>; 127 using OmpxDynCgroupMem = tomp::clause::OmpxDynCgroupMemT<TypeTy, IdTy, ExprTy>; 128 using Ordered = tomp::clause::OrderedT<TypeTy, IdTy, ExprTy>; 129 using Order = tomp::clause::OrderT<TypeTy, IdTy, ExprTy>; 130 using Partial = tomp::clause::PartialT<TypeTy, IdTy, ExprTy>; 131 using Priority = tomp::clause::PriorityT<TypeTy, IdTy, ExprTy>; 132 using Private = tomp::clause::PrivateT<TypeTy, IdTy, ExprTy>; 133 using ProcBind = tomp::clause::ProcBindT<TypeTy, IdTy, ExprTy>; 134 using Read = tomp::clause::ReadT<TypeTy, IdTy, ExprTy>; 135 using Reduction = tomp::clause::ReductionT<TypeTy, IdTy, ExprTy>; 136 using Relaxed = tomp::clause::RelaxedT<TypeTy, IdTy, ExprTy>; 137 using Release = tomp::clause::ReleaseT<TypeTy, IdTy, ExprTy>; 138 using ReverseOffload = tomp::clause::ReverseOffloadT<TypeTy, IdTy, ExprTy>; 139 using Safelen = tomp::clause::SafelenT<TypeTy, IdTy, ExprTy>; 140 using Schedule = tomp::clause::ScheduleT<TypeTy, IdTy, ExprTy>; 141 using SeqCst = tomp::clause::SeqCstT<TypeTy, IdTy, ExprTy>; 142 using Severity = tomp::clause::SeverityT<TypeTy, IdTy, ExprTy>; 143 using Shared = tomp::clause::SharedT<TypeTy, IdTy, ExprTy>; 144 using Simdlen = tomp::clause::SimdlenT<TypeTy, IdTy, ExprTy>; 145 using Simd = tomp::clause::SimdT<TypeTy, IdTy, ExprTy>; 146 using Sizes = tomp::clause::SizesT<TypeTy, IdTy, ExprTy>; 147 using TaskReduction = tomp::clause::TaskReductionT<TypeTy, IdTy, ExprTy>; 148 using ThreadLimit = tomp::clause::ThreadLimitT<TypeTy, IdTy, ExprTy>; 149 using Threads = tomp::clause::ThreadsT<TypeTy, IdTy, ExprTy>; 150 using To = tomp::clause::ToT<TypeTy, IdTy, ExprTy>; 151 using UnifiedAddress = tomp::clause::UnifiedAddressT<TypeTy, IdTy, ExprTy>; 152 using UnifiedSharedMemory = 153 tomp::clause::UnifiedSharedMemoryT<TypeTy, IdTy, ExprTy>; 154 using Uniform = tomp::clause::UniformT<TypeTy, IdTy, ExprTy>; 155 using Unknown = tomp::clause::UnknownT<TypeTy, IdTy, ExprTy>; 156 using Untied = tomp::clause::UntiedT<TypeTy, IdTy, ExprTy>; 157 using Update = tomp::clause::UpdateT<TypeTy, IdTy, ExprTy>; 158 using UseDeviceAddr = tomp::clause::UseDeviceAddrT<TypeTy, IdTy, ExprTy>; 159 using UseDevicePtr = tomp::clause::UseDevicePtrT<TypeTy, IdTy, ExprTy>; 160 using UsesAllocators = tomp::clause::UsesAllocatorsT<TypeTy, IdTy, ExprTy>; 161 using Use = tomp::clause::UseT<TypeTy, IdTy, ExprTy>; 162 using Weak = tomp::clause::WeakT<TypeTy, IdTy, ExprTy>; 163 using When = tomp::clause::WhenT<TypeTy, IdTy, ExprTy>; 164 using Write = tomp::clause::WriteT<TypeTy, IdTy, ExprTy>; 165 } // namespace clause 166 167 struct Helper { 168 std::optional<Object> getBaseObject(const Object &object) { 169 return std::nullopt; 170 } 171 std::optional<Object> getLoopIterVar() { return std::nullopt; } 172 }; 173 174 using Clause = tomp::ClauseT<TypeTy, IdTy, ExprTy>; 175 using ConstructDecomposition = tomp::ConstructDecompositionT<Clause, Helper>; 176 using DirectiveWithClauses = tomp::DirectiveWithClauses<Clause>; 177 } // namespace omp 178 179 struct StringifyClause { 180 static std::string join(const omp::List<std::string> &Strings) { 181 std::stringstream Stream; 182 for (const auto &[Index, String] : llvm::enumerate(Strings)) { 183 if (Index != 0) 184 Stream << ", "; 185 Stream << String; 186 } 187 return Stream.str(); 188 } 189 190 static std::string to_str(llvm::omp::Directive D) { 191 return getOpenMPDirectiveName(D).str(); 192 } 193 static std::string to_str(llvm::omp::Clause C) { 194 return getOpenMPClauseName(C).str(); 195 } 196 static std::string to_str(const omp::TypeTy &Type) { return "type"; } 197 static std::string to_str(const omp::ExprTy &Expr) { return "expr"; } 198 static std::string to_str(const omp::Object &Obj) { return Obj.id(); } 199 200 template <typename U> 201 static std::enable_if_t<std::is_enum_v<llvm::remove_cvref_t<U>>, std::string> 202 to_str(U &&Item) { 203 return std::to_string(llvm::to_underlying(Item)); 204 } 205 206 template <typename U> static std::string to_str(const omp::List<U> &Items) { 207 omp::List<std::string> Names; 208 llvm::transform(Items, std::back_inserter(Names), 209 [](auto &&S) { return to_str(S); }); 210 return "(" + join(Names) + ")"; 211 } 212 213 template <typename U> 214 static std::string to_str(const std::optional<U> &Item) { 215 if (Item) 216 return to_str(*Item); 217 return ""; 218 } 219 220 template <typename... Us, size_t... Is> 221 static std::string to_str(const std::tuple<Us...> &Tuple, 222 std::index_sequence<Is...>) { 223 omp::List<std::string> Strings; 224 (Strings.push_back(to_str(std::get<Is>(Tuple))), ...); 225 return "(" + join(Strings) + ")"; 226 } 227 228 template <typename U> 229 static std::enable_if_t<llvm::remove_cvref_t<U>::EmptyTrait::value, 230 std::string> 231 to_str(U &&Item) { 232 return ""; 233 } 234 235 template <typename U> 236 static std::enable_if_t<llvm::remove_cvref_t<U>::IncompleteTrait::value, 237 std::string> 238 to_str(U &&Item) { 239 return ""; 240 } 241 242 template <typename U> 243 static std::enable_if_t<llvm::remove_cvref_t<U>::WrapperTrait::value, 244 std::string> 245 to_str(U &&Item) { 246 // For a wrapper, stringify the wrappee, and only add parentheses if 247 // there aren't any already. 248 std::string Str = to_str(Item.v); 249 if (!Str.empty()) { 250 if (Str.front() == '(' && Str.back() == ')') 251 return Str; 252 } 253 return "(" + to_str(Item.v) + ")"; 254 } 255 256 template <typename U> 257 static std::enable_if_t<llvm::remove_cvref_t<U>::TupleTrait::value, 258 std::string> 259 to_str(U &&Item) { 260 constexpr size_t TupleSize = 261 std::tuple_size_v<llvm::remove_cvref_t<decltype(Item.t)>>; 262 return to_str(Item.t, std::make_index_sequence<TupleSize>{}); 263 } 264 265 template <typename U> 266 static std::enable_if_t<llvm::remove_cvref_t<U>::UnionTrait::value, 267 std::string> 268 to_str(U &&Item) { 269 return std::visit([](auto &&S) { return to_str(S); }, Item.u); 270 } 271 272 StringifyClause(const omp::Clause &C) 273 // Rely on content stringification to emit enclosing parentheses. 274 : Str(to_str(C.id) + to_str(C)) {} 275 276 std::string Str; 277 }; 278 279 std::string stringify(const omp::DirectiveWithClauses &DWC) { 280 std::stringstream Stream; 281 282 Stream << getOpenMPDirectiveName(DWC.id).str(); 283 for (const omp::Clause &C : DWC.clauses) 284 Stream << ' ' << StringifyClause(C).Str; 285 286 return Stream.str(); 287 } 288 289 // --- Tests ---------------------------------------------------------- 290 291 namespace red { 292 // Make it easier to construct reduction operators from built-in intrinsics. 293 omp::clause::ReductionOperator 294 makeOp(omp::clause::DefinedOperator::IntrinsicOperator Op) { 295 return omp::clause::ReductionOperator{omp::clause::DefinedOperator{Op}}; 296 } 297 } // namespace red 298 299 namespace { 300 using namespace llvm::omp; 301 302 class OpenMPDecompositionTest : public testing::Test { 303 protected: 304 void SetUp() override {} 305 void TearDown() override {} 306 307 omp::Helper Helper; 308 uint32_t AnyVersion = 999; 309 }; 310 311 // PRIVATE 312 // [5.2:111:5-7] 313 // Directives: distribute, do, for, loop, parallel, scope, sections, simd, 314 // single, target, task, taskloop, teams 315 // 316 // [5.2:340:1-2] 317 // (1) The effect of the 1 private clause is as if it is applied only to the 318 // innermost leaf construct that permits it. 319 TEST_F(OpenMPDecompositionTest, Private1) { 320 omp::Object x{"x"}; 321 322 omp::List<omp::Clause> Clauses{ 323 {OMPC_private, omp::clause::Private{{x}}}, 324 }; 325 326 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections, 327 Clauses); 328 ASSERT_EQ(Dec.output.size(), 2u); 329 330 std::string Dir0 = stringify(Dec.output[0]); 331 std::string Dir1 = stringify(Dec.output[1]); 332 ASSERT_EQ(Dir0, "parallel"); // (1) 333 ASSERT_EQ(Dir1, "sections private(x)"); // (1) 334 } 335 336 TEST_F(OpenMPDecompositionTest, Private2) { 337 omp::Object x{"x"}; 338 339 omp::List<omp::Clause> Clauses{ 340 {OMPC_private, omp::clause::Private{{x}}}, 341 }; 342 343 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_masked, 344 Clauses); 345 ASSERT_EQ(Dec.output.size(), 2u); 346 347 std::string Dir0 = stringify(Dec.output[0]); 348 std::string Dir1 = stringify(Dec.output[1]); 349 ASSERT_EQ(Dir0, "parallel private(x)"); // (1) 350 ASSERT_EQ(Dir1, "masked"); // (1) 351 } 352 353 // FIRSTPRIVATE 354 // [5.2:112:5-7] 355 // Directives: distribute, do, for, parallel, scope, sections, single, target, 356 // task, taskloop, teams 357 // 358 // [5.2:340:3-20] 359 // (3) The effect of the firstprivate clause is as if it is applied to one or 360 // more leaf constructs as follows: 361 // (5) To the distribute construct if it is among the constituent constructs; 362 // (6) To the teams construct if it is among the constituent constructs and the 363 // distribute construct is not; 364 // (8) To a worksharing construct that accepts the clause if one is among the 365 // constituent constructs; 366 // (9) To the taskloop construct if it is among the constituent constructs; 367 // (10) To the parallel construct if it is among the constituent constructs and 368 // neither a taskloop construct nor a worksharing construct that accepts 369 // the clause is among them; 370 // (12) To the target construct if it is among the constituent constructs and 371 // the same list item neither appears in a lastprivate clause nor is the 372 // base variable or base pointer of a list item that appears in a map 373 // clause. 374 // 375 // (15) If the parallel construct is among the constituent constructs and the 376 // effect is not as if the firstprivate clause is applied to it by the above 377 // rules, then the effect is as if the shared clause with the same list item is 378 // applied to the parallel construct. 379 // (17) If the teams construct is among the constituent constructs and the 380 // effect is not as if the firstprivate clause is applied to it by the above 381 // rules, then the effect is as if the shared clause with the same list item is 382 // applied to the teams construct. 383 TEST_F(OpenMPDecompositionTest, Firstprivate1) { 384 omp::Object x{"x"}; 385 386 omp::List<omp::Clause> Clauses{ 387 {OMPC_firstprivate, omp::clause::Firstprivate{{x}}}, 388 }; 389 390 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections, 391 Clauses); 392 ASSERT_EQ(Dec.output.size(), 2u); 393 394 std::string Dir0 = stringify(Dec.output[0]); 395 std::string Dir1 = stringify(Dec.output[1]); 396 ASSERT_EQ(Dir0, "parallel shared(x)"); // (10), (15) 397 ASSERT_EQ(Dir1, "sections firstprivate(x)"); // (8) 398 } 399 400 TEST_F(OpenMPDecompositionTest, Firstprivate2) { 401 omp::Object x{"x"}; 402 403 omp::List<omp::Clause> Clauses{ 404 {OMPC_firstprivate, omp::clause::Firstprivate{{x}}}, 405 }; 406 407 omp::ConstructDecomposition Dec(AnyVersion, Helper, 408 OMPD_target_teams_distribute, Clauses); 409 ASSERT_EQ(Dec.output.size(), 3u); 410 411 std::string Dir0 = stringify(Dec.output[0]); 412 std::string Dir1 = stringify(Dec.output[1]); 413 std::string Dir2 = stringify(Dec.output[2]); 414 ASSERT_EQ(Dir0, "target firstprivate(x)"); // (12) 415 ASSERT_EQ(Dir1, "teams shared(x)"); // (6), (17) 416 ASSERT_EQ(Dir2, "distribute firstprivate(x)"); // (5) 417 } 418 419 TEST_F(OpenMPDecompositionTest, Firstprivate3) { 420 omp::Object x{"x"}; 421 422 omp::List<omp::Clause> Clauses{ 423 {OMPC_firstprivate, omp::clause::Firstprivate{{x}}}, 424 {OMPC_lastprivate, omp::clause::Lastprivate{{std::nullopt, {x}}}}, 425 }; 426 427 omp::ConstructDecomposition Dec(AnyVersion, Helper, 428 OMPD_target_teams_distribute, Clauses); 429 ASSERT_EQ(Dec.output.size(), 3u); 430 431 std::string Dir0 = stringify(Dec.output[0]); 432 std::string Dir1 = stringify(Dec.output[1]); 433 std::string Dir2 = stringify(Dec.output[2]); 434 ASSERT_EQ(Dir0, "target map(2, , , , (x))"); // (12), (27) 435 ASSERT_EQ(Dir1, "teams shared(x)"); // (6), (17) 436 ASSERT_EQ(Dir2, "distribute firstprivate(x) lastprivate(, (x))"); // (5), (21) 437 } 438 439 TEST_F(OpenMPDecompositionTest, Firstprivate4) { 440 omp::Object x{"x"}; 441 442 omp::List<omp::Clause> Clauses{ 443 {OMPC_firstprivate, omp::clause::Firstprivate{{x}}}, 444 }; 445 446 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_target_teams, 447 Clauses); 448 ASSERT_EQ(Dec.output.size(), 2u); 449 450 std::string Dir0 = stringify(Dec.output[0]); 451 std::string Dir1 = stringify(Dec.output[1]); 452 ASSERT_EQ(Dir0, "target firstprivate(x)"); // (12) 453 ASSERT_EQ(Dir1, "teams firstprivate(x)"); // (6) 454 } 455 456 TEST_F(OpenMPDecompositionTest, Firstprivate5) { 457 omp::Object x{"x"}; 458 459 omp::List<omp::Clause> Clauses{ 460 {OMPC_firstprivate, omp::clause::Firstprivate{{x}}}, 461 }; 462 463 omp::ConstructDecomposition Dec(AnyVersion, Helper, 464 OMPD_parallel_masked_taskloop, Clauses); 465 ASSERT_EQ(Dec.output.size(), 3u); 466 467 std::string Dir0 = stringify(Dec.output[0]); 468 std::string Dir1 = stringify(Dec.output[1]); 469 std::string Dir2 = stringify(Dec.output[2]); 470 ASSERT_EQ(Dir0, "parallel shared(x)"); // (10) 471 ASSERT_EQ(Dir1, "masked"); 472 ASSERT_EQ(Dir2, "taskloop firstprivate(x)"); // (9) 473 } 474 475 TEST_F(OpenMPDecompositionTest, Firstprivate6) { 476 omp::Object x{"x"}; 477 478 omp::List<omp::Clause> Clauses{ 479 {OMPC_firstprivate, omp::clause::Firstprivate{{x}}}, 480 }; 481 482 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_masked, 483 Clauses); 484 ASSERT_EQ(Dec.output.size(), 2u); 485 486 std::string Dir0 = stringify(Dec.output[0]); 487 std::string Dir1 = stringify(Dec.output[1]); 488 ASSERT_EQ(Dir0, "parallel firstprivate(x)"); // (10) 489 ASSERT_EQ(Dir1, "masked"); 490 } 491 492 TEST_F(OpenMPDecompositionTest, Firstprivate7) { 493 omp::Object x{"x"}; 494 495 omp::List<omp::Clause> Clauses{ 496 {OMPC_firstprivate, omp::clause::Firstprivate{{x}}}, 497 }; 498 499 // Composite constructs are still decomposed. 500 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_teams_distribute, 501 Clauses); 502 ASSERT_EQ(Dec.output.size(), 2u); 503 504 std::string Dir0 = stringify(Dec.output[0]); 505 std::string Dir1 = stringify(Dec.output[1]); 506 ASSERT_EQ(Dir0, "teams shared(x)"); // (17) 507 ASSERT_EQ(Dir1, "distribute firstprivate(x)"); // (5) 508 } 509 510 // LASTPRIVATE 511 // [5.2:115:7-8] 512 // Directives: distribute, do, for, loop, sections, simd, taskloop 513 // 514 // [5.2:340:21-30] 515 // (21) The effect of the lastprivate clause is as if it is applied to all leaf 516 // constructs that permit the clause. 517 // (22) If the parallel construct is among the constituent constructs and the 518 // list item is not also specified in the firstprivate clause, then the effect 519 // of the lastprivate clause is as if the shared clause with the same list item 520 // is applied to the parallel construct. 521 // (24) If the teams construct is among the constituent constructs and the list 522 // item is not also specified in the firstprivate clause, then the effect of the 523 // lastprivate clause is as if the shared clause with the same list item is 524 // applied to the teams construct. 525 // (27) If the target construct is among the constituent constructs and the list 526 // item is not the base variable or base pointer of a list item that appears in 527 // a map clause, the effect of the lastprivate clause is as if the same list 528 // item appears in a map clause with a map-type of tofrom. 529 TEST_F(OpenMPDecompositionTest, Lastprivate1) { 530 omp::Object x{"x"}; 531 532 omp::List<omp::Clause> Clauses{ 533 {OMPC_lastprivate, omp::clause::Lastprivate{{std::nullopt, {x}}}}, 534 }; 535 536 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections, 537 Clauses); 538 ASSERT_EQ(Dec.output.size(), 2u); 539 540 std::string Dir0 = stringify(Dec.output[0]); 541 std::string Dir1 = stringify(Dec.output[1]); 542 ASSERT_EQ(Dir0, "parallel shared(x)"); // (21), (22) 543 ASSERT_EQ(Dir1, "sections lastprivate(, (x))"); // (21) 544 } 545 546 TEST_F(OpenMPDecompositionTest, Lastprivate2) { 547 omp::Object x{"x"}; 548 549 omp::List<omp::Clause> Clauses{ 550 {OMPC_lastprivate, omp::clause::Lastprivate{{std::nullopt, {x}}}}, 551 }; 552 553 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_teams_distribute, 554 Clauses); 555 ASSERT_EQ(Dec.output.size(), 2u); 556 557 std::string Dir0 = stringify(Dec.output[0]); 558 std::string Dir1 = stringify(Dec.output[1]); 559 ASSERT_EQ(Dir0, "teams shared(x)"); // (21), (25) 560 ASSERT_EQ(Dir1, "distribute lastprivate(, (x))"); // (21) 561 } 562 563 TEST_F(OpenMPDecompositionTest, Lastprivate3) { 564 omp::Object x{"x"}; 565 566 omp::List<omp::Clause> Clauses{ 567 {OMPC_lastprivate, omp::clause::Lastprivate{{std::nullopt, {x}}}}, 568 }; 569 570 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_target_parallel_do, 571 Clauses); 572 ASSERT_EQ(Dec.output.size(), 3u); 573 574 std::string Dir0 = stringify(Dec.output[0]); 575 std::string Dir1 = stringify(Dec.output[1]); 576 std::string Dir2 = stringify(Dec.output[2]); 577 ASSERT_EQ(Dir0, "target map(2, , , , (x))"); // (21), (27) 578 ASSERT_EQ(Dir1, "parallel shared(x)"); // (22) 579 ASSERT_EQ(Dir2, "do lastprivate(, (x))"); // (21) 580 } 581 582 // SHARED 583 // [5.2:110:5-6] 584 // Directives: parallel, task, taskloop, teams 585 // 586 // [5.2:340:31-32] 587 // (31) The effect of the shared, default, thread_limit, or order clause is as 588 // if it is applied to all leaf constructs that permit the clause. 589 TEST_F(OpenMPDecompositionTest, Shared1) { 590 omp::Object x{"x"}; 591 592 omp::List<omp::Clause> Clauses{ 593 {OMPC_shared, omp::clause::Shared{{x}}}, 594 }; 595 596 omp::ConstructDecomposition Dec(AnyVersion, Helper, 597 OMPD_parallel_masked_taskloop, Clauses); 598 ASSERT_EQ(Dec.output.size(), 3u); 599 600 std::string Dir0 = stringify(Dec.output[0]); 601 std::string Dir1 = stringify(Dec.output[1]); 602 std::string Dir2 = stringify(Dec.output[2]); 603 ASSERT_EQ(Dir0, "parallel shared(x)"); // (31) 604 ASSERT_EQ(Dir1, "masked"); // (31) 605 ASSERT_EQ(Dir2, "taskloop shared(x)"); // (31) 606 } 607 608 // DEFAULT 609 // [5.2:109:5-6] 610 // Directives: parallel, task, taskloop, teams 611 // 612 // [5.2:340:31-32] 613 // (31) The effect of the shared, default, thread_limit, or order clause is as 614 // if it is applied to all leaf constructs that permit the clause. 615 TEST_F(OpenMPDecompositionTest, Default1) { 616 omp::Object x{"x"}; 617 618 omp::List<omp::Clause> Clauses{ 619 {OMPC_default, 620 omp::clause::Default{ 621 omp::clause::Default::DataSharingAttribute::Firstprivate}}, 622 }; 623 624 omp::ConstructDecomposition Dec(AnyVersion, Helper, 625 OMPD_parallel_masked_taskloop, Clauses); 626 ASSERT_EQ(Dec.output.size(), 3u); 627 628 std::string Dir0 = stringify(Dec.output[0]); 629 std::string Dir1 = stringify(Dec.output[1]); 630 std::string Dir2 = stringify(Dec.output[2]); 631 ASSERT_EQ(Dir0, "parallel default(0)"); // (31) 632 ASSERT_EQ(Dir1, "masked"); // (31) 633 ASSERT_EQ(Dir2, "taskloop default(0)"); // (31) 634 } 635 636 // THREAD_LIMIT 637 // [5.2:277:14-15] 638 // Directives: target, teams 639 // 640 // [5.2:340:31-32] 641 // (31) The effect of the shared, default, thread_limit, or order clause is as 642 // if it is applied to all leaf constructs that permit the clause. 643 TEST_F(OpenMPDecompositionTest, ThreadLimit1) { 644 omp::Object x{"x"}; 645 646 omp::List<omp::Clause> Clauses{ 647 {OMPC_thread_limit, omp::clause::ThreadLimit{omp::ExprTy{}}}, 648 }; 649 650 omp::ConstructDecomposition Dec(AnyVersion, Helper, 651 OMPD_target_teams_distribute, Clauses); 652 ASSERT_EQ(Dec.output.size(), 3u); 653 654 std::string Dir0 = stringify(Dec.output[0]); 655 std::string Dir1 = stringify(Dec.output[1]); 656 std::string Dir2 = stringify(Dec.output[2]); 657 ASSERT_EQ(Dir0, "target thread_limit(expr)"); // (31) 658 ASSERT_EQ(Dir1, "teams thread_limit(expr)"); // (31) 659 ASSERT_EQ(Dir2, "distribute"); // (31) 660 } 661 662 // ORDER 663 // [5.2:234:3-4] 664 // Directives: distribute, do, for, loop, simd 665 // 666 // [5.2:340:31-32] 667 // (31) The effect of the shared, default, thread_limit, or order clause is as 668 // if it is applied to all leaf constructs that permit the clause. 669 TEST_F(OpenMPDecompositionTest, Order1) { 670 omp::Object x{"x"}; 671 672 omp::List<omp::Clause> Clauses{ 673 {OMPC_order, 674 omp::clause::Order{{omp::clause::Order::OrderModifier::Unconstrained, 675 omp::clause::Order::Ordering::Concurrent}}}, 676 }; 677 678 omp::ConstructDecomposition Dec( 679 AnyVersion, Helper, OMPD_target_teams_distribute_parallel_for_simd, 680 Clauses); 681 ASSERT_EQ(Dec.output.size(), 6u); 682 683 std::string Dir0 = stringify(Dec.output[0]); 684 std::string Dir1 = stringify(Dec.output[1]); 685 std::string Dir2 = stringify(Dec.output[2]); 686 std::string Dir3 = stringify(Dec.output[3]); 687 std::string Dir4 = stringify(Dec.output[4]); 688 std::string Dir5 = stringify(Dec.output[5]); 689 ASSERT_EQ(Dir0, "target"); // (31) 690 ASSERT_EQ(Dir1, "teams"); // (31) 691 ASSERT_EQ(Dir2, "distribute order(1, 0)"); // (31) 692 ASSERT_EQ(Dir3, "parallel"); // (31) 693 ASSERT_EQ(Dir4, "for order(1, 0)"); // (31) 694 ASSERT_EQ(Dir5, "simd order(1, 0)"); // (31) 695 } 696 697 // ALLOCATE 698 // [5.2:178:7-9] 699 // Directives: allocators, distribute, do, for, parallel, scope, sections, 700 // single, target, task, taskgroup, taskloop, teams 701 // 702 // [5.2:340:33-35] 703 // (33) The effect of the allocate clause is as if it is applied to all leaf 704 // constructs that permit the clause and to which a data-sharing attribute 705 // clause that may create a private copy of the same list item is applied. 706 TEST_F(OpenMPDecompositionTest, Allocate1) { 707 omp::Object x{"x"}; 708 709 // Allocate + firstprivate 710 omp::List<omp::Clause> Clauses{ 711 {OMPC_allocate, 712 omp::clause::Allocate{{std::nullopt, std::nullopt, std::nullopt, {x}}}}, 713 {OMPC_firstprivate, omp::clause::Firstprivate{{x}}}, 714 }; 715 716 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections, 717 Clauses); 718 ASSERT_EQ(Dec.output.size(), 2u); 719 720 std::string Dir0 = stringify(Dec.output[0]); 721 std::string Dir1 = stringify(Dec.output[1]); 722 ASSERT_EQ(Dir0, "parallel shared(x)"); // (33) 723 ASSERT_EQ(Dir1, "sections firstprivate(x) allocate(, , , (x))"); // (33) 724 } 725 726 TEST_F(OpenMPDecompositionTest, Allocate2) { 727 omp::Object x{"x"}; 728 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 729 730 // Allocate + in_reduction 731 omp::List<omp::Clause> Clauses{ 732 {OMPC_allocate, 733 omp::clause::Allocate{{std::nullopt, std::nullopt, std::nullopt, {x}}}}, 734 {OMPC_in_reduction, omp::clause::InReduction{{{Add}, {x}}}}, 735 }; 736 737 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_target_parallel, 738 Clauses); 739 ASSERT_EQ(Dec.output.size(), 2u); 740 741 std::string Dir0 = stringify(Dec.output[0]); 742 std::string Dir1 = stringify(Dec.output[1]); 743 ASSERT_EQ(Dir0, "target in_reduction((3), (x)) allocate(, , , (x))"); // (33) 744 ASSERT_EQ(Dir1, "parallel"); // (33) 745 } 746 747 TEST_F(OpenMPDecompositionTest, Allocate3) { 748 omp::Object x{"x"}; 749 750 // Allocate + linear 751 omp::List<omp::Clause> Clauses{ 752 {OMPC_allocate, 753 omp::clause::Allocate{{std::nullopt, std::nullopt, std::nullopt, {x}}}}, 754 {OMPC_linear, 755 omp::clause::Linear{{std::nullopt, std::nullopt, std::nullopt, {x}}}}, 756 }; 757 758 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_for, 759 Clauses); 760 ASSERT_EQ(Dec.output.size(), 2u); 761 762 std::string Dir0 = stringify(Dec.output[0]); 763 std::string Dir1 = stringify(Dec.output[1]); 764 // The "shared" clause is duplicated---this isn't harmful, but it 765 // should be fixed eventually. 766 ASSERT_EQ(Dir0, "parallel shared(x) shared(x)"); // (33) 767 ASSERT_EQ(Dir1, "for linear(, , , (x)) firstprivate(x) lastprivate(, (x)) " 768 "allocate(, , , (x))"); // (33) 769 } 770 771 TEST_F(OpenMPDecompositionTest, Allocate4) { 772 omp::Object x{"x"}; 773 774 // Allocate + lastprivate 775 omp::List<omp::Clause> Clauses{ 776 {OMPC_allocate, 777 omp::clause::Allocate{{std::nullopt, std::nullopt, std::nullopt, {x}}}}, 778 {OMPC_lastprivate, omp::clause::Lastprivate{{std::nullopt, {x}}}}, 779 }; 780 781 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections, 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 ASSERT_EQ(Dir0, "parallel shared(x)"); // (33) 788 ASSERT_EQ(Dir1, "sections lastprivate(, (x)) allocate(, , , (x))"); // (33) 789 } 790 791 TEST_F(OpenMPDecompositionTest, Allocate5) { 792 omp::Object x{"x"}; 793 794 // Allocate + private 795 omp::List<omp::Clause> Clauses{ 796 {OMPC_allocate, 797 omp::clause::Allocate{{std::nullopt, std::nullopt, std::nullopt, {x}}}}, 798 {OMPC_private, omp::clause::Private{{x}}}, 799 }; 800 801 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections, 802 Clauses); 803 ASSERT_EQ(Dec.output.size(), 2u); 804 805 std::string Dir0 = stringify(Dec.output[0]); 806 std::string Dir1 = stringify(Dec.output[1]); 807 ASSERT_EQ(Dir0, "parallel"); // (33) 808 ASSERT_EQ(Dir1, "sections private(x) allocate(, , , (x))"); // (33) 809 } 810 811 TEST_F(OpenMPDecompositionTest, Allocate6) { 812 omp::Object x{"x"}; 813 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 814 815 // Allocate + reduction 816 omp::List<omp::Clause> Clauses{ 817 {OMPC_allocate, 818 omp::clause::Allocate{{std::nullopt, std::nullopt, std::nullopt, {x}}}}, 819 {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {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 shared(x)"); // (33) 829 ASSERT_EQ(Dir1, "sections reduction(, (3), (x)) allocate(, , , (x))"); // (33) 830 } 831 832 // REDUCTION 833 // [5.2:134:17-18] 834 // Directives: do, for, loop, parallel, scope, sections, simd, taskloop, teams 835 // 836 // [5.2:340-341:36-13] 837 // (36) The effect of the reduction clause is as if it is applied to all leaf 838 // constructs that permit the clause, except for the following constructs: 839 // (1) The parallel construct, when combined with the sections, 840 // worksharing-loop, loop, or taskloop construct; and 841 // (3) The teams construct, when combined with the loop construct. 842 // (4) For the parallel and teams constructs above, the effect of the reduction 843 // clause instead is as if each list item or, for any list item that is an array 844 // item, its corresponding base array or base pointer appears in a shared clause 845 // for the construct. 846 // (6) If the task reduction-modifier is specified, the effect is as if it only 847 // modifies the behavior of the reduction clause on the innermost leaf construct 848 // that accepts the modifier (see Section 5.5.8). 849 // (8) If the inscan reduction-modifier is specified, the effect is as if it 850 // modifies the behavior of the reduction clause on all constructs of the 851 // combined construct to which the clause is applied and that accept the 852 // modifier. 853 // (10) If a list item in a reduction clause on a combined target construct does 854 // not have the same base variable or base pointer as a list item in a map 855 // clause on the construct, then the effect is as if the list item in the 856 // reduction clause appears as a list item in a map clause with a map-type of 857 // tofrom. 858 TEST_F(OpenMPDecompositionTest, Reduction1) { 859 omp::Object x{"x"}; 860 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 861 862 omp::List<omp::Clause> Clauses{ 863 {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}}, 864 }; 865 866 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections, 867 Clauses); 868 ASSERT_EQ(Dec.output.size(), 2u); 869 870 std::string Dir0 = stringify(Dec.output[0]); 871 std::string Dir1 = stringify(Dec.output[1]); 872 ASSERT_EQ(Dir0, "parallel shared(x)"); // (36), (1), (4) 873 ASSERT_EQ(Dir1, "sections reduction(, (3), (x))"); // (36) 874 } 875 876 TEST_F(OpenMPDecompositionTest, Reduction2) { 877 omp::Object x{"x"}; 878 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 879 880 omp::List<omp::Clause> Clauses{ 881 {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}}, 882 }; 883 884 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_masked, 885 Clauses); 886 ASSERT_EQ(Dec.output.size(), 2u); 887 888 std::string Dir0 = stringify(Dec.output[0]); 889 std::string Dir1 = stringify(Dec.output[1]); 890 ASSERT_EQ(Dir0, "parallel reduction(, (3), (x))"); // (36), (1), (4) 891 ASSERT_EQ(Dir1, "masked"); // (36) 892 } 893 894 TEST_F(OpenMPDecompositionTest, Reduction3) { 895 omp::Object x{"x"}; 896 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 897 898 omp::List<omp::Clause> Clauses{ 899 {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}}, 900 }; 901 902 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_teams_loop, Clauses); 903 ASSERT_EQ(Dec.output.size(), 2u); 904 905 std::string Dir0 = stringify(Dec.output[0]); 906 std::string Dir1 = stringify(Dec.output[1]); 907 ASSERT_EQ(Dir0, "teams shared(x)"); // (36), (3), (4) 908 ASSERT_EQ(Dir1, "loop reduction(, (3), (x))"); // (36) 909 } 910 911 TEST_F(OpenMPDecompositionTest, Reduction4) { 912 omp::Object x{"x"}; 913 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 914 915 omp::List<omp::Clause> Clauses{ 916 {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}}, 917 }; 918 919 omp::ConstructDecomposition Dec(AnyVersion, Helper, 920 OMPD_teams_distribute_parallel_for, Clauses); 921 ASSERT_EQ(Dec.output.size(), 4u); 922 923 std::string Dir0 = stringify(Dec.output[0]); 924 std::string Dir1 = stringify(Dec.output[1]); 925 std::string Dir2 = stringify(Dec.output[2]); 926 std::string Dir3 = stringify(Dec.output[3]); 927 ASSERT_EQ(Dir0, "teams reduction(, (3), (x))"); // (36), (3) 928 ASSERT_EQ(Dir1, "distribute"); // (36) 929 ASSERT_EQ(Dir2, "parallel shared(x)"); // (36), (1), (4) 930 ASSERT_EQ(Dir3, "for reduction(, (3), (x))"); // (36) 931 } 932 933 TEST_F(OpenMPDecompositionTest, Reduction5) { 934 omp::Object x{"x"}; 935 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 936 auto TaskMod = omp::clause::Reduction::ReductionModifier::Task; 937 938 omp::List<omp::Clause> Clauses{ 939 {OMPC_reduction, omp::clause::Reduction{{TaskMod, {Add}, {x}}}}, 940 }; 941 942 omp::ConstructDecomposition Dec(AnyVersion, Helper, 943 OMPD_teams_distribute_parallel_for, Clauses); 944 ASSERT_EQ(Dec.output.size(), 4u); 945 946 std::string Dir0 = stringify(Dec.output[0]); 947 std::string Dir1 = stringify(Dec.output[1]); 948 std::string Dir2 = stringify(Dec.output[2]); 949 std::string Dir3 = stringify(Dec.output[3]); 950 ASSERT_EQ(Dir0, "teams reduction(, (3), (x))"); // (36), (3), (6) 951 ASSERT_EQ(Dir1, "distribute"); // (36) 952 ASSERT_EQ(Dir2, "parallel shared(x)"); // (36), (1), (4) 953 ASSERT_EQ(Dir3, "for reduction(2, (3), (x))"); // (36), (6) 954 } 955 956 TEST_F(OpenMPDecompositionTest, Reduction6) { 957 omp::Object x{"x"}; 958 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 959 auto InscanMod = omp::clause::Reduction::ReductionModifier::Inscan; 960 961 omp::List<omp::Clause> Clauses{ 962 {OMPC_reduction, omp::clause::Reduction{{InscanMod, {Add}, {x}}}}, 963 }; 964 965 omp::ConstructDecomposition Dec(AnyVersion, Helper, 966 OMPD_teams_distribute_parallel_for, Clauses); 967 ASSERT_EQ(Dec.output.size(), 4u); 968 969 std::string Dir0 = stringify(Dec.output[0]); 970 std::string Dir1 = stringify(Dec.output[1]); 971 std::string Dir2 = stringify(Dec.output[2]); 972 std::string Dir3 = stringify(Dec.output[3]); 973 ASSERT_EQ(Dir0, "teams reduction(, (3), (x))"); // (36), (3), (8) 974 ASSERT_EQ(Dir1, "distribute"); // (36) 975 ASSERT_EQ(Dir2, "parallel shared(x)"); // (36), (1), (4) 976 ASSERT_EQ(Dir3, "for reduction(1, (3), (x))"); // (36), (8) 977 } 978 979 TEST_F(OpenMPDecompositionTest, Reduction7) { 980 omp::Object x{"x"}; 981 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 982 983 omp::List<omp::Clause> Clauses{ 984 {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}}, 985 }; 986 987 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_target_parallel_do, 988 Clauses); 989 ASSERT_EQ(Dec.output.size(), 3u); 990 991 std::string Dir0 = stringify(Dec.output[0]); 992 std::string Dir1 = stringify(Dec.output[1]); 993 std::string Dir2 = stringify(Dec.output[2]); 994 ASSERT_EQ(Dir0, "target map(2, , , , (x))"); // (36), (10) 995 ASSERT_EQ(Dir1, "parallel shared(x)"); // (36), (1), (4) 996 ASSERT_EQ(Dir2, "do reduction(, (3), (x))"); // (36) 997 } 998 999 // IF 1000 // [5.2:72:7-9] 1001 // Directives: cancel, parallel, simd, target, target data, target enter data, 1002 // target exit data, target update, task, taskloop 1003 // 1004 // [5.2:72:15-18] 1005 // (15) For combined or composite constructs, the if clause only applies to the 1006 // semantics of the construct named in the directive-name-modifier. 1007 // (16) For a combined or composite construct, if no directive-name-modifier is 1008 // specified then the if clause applies to all constituent constructs to which 1009 // an if clause can apply. 1010 TEST_F(OpenMPDecompositionTest, If1) { 1011 omp::List<omp::Clause> Clauses{ 1012 {OMPC_if, 1013 omp::clause::If{{llvm::omp::Directive::OMPD_parallel, omp::ExprTy{}}}}, 1014 }; 1015 1016 omp::ConstructDecomposition Dec(AnyVersion, Helper, 1017 OMPD_target_parallel_for_simd, Clauses); 1018 ASSERT_EQ(Dec.output.size(), 4u); 1019 std::string Dir0 = stringify(Dec.output[0]); 1020 std::string Dir1 = stringify(Dec.output[1]); 1021 std::string Dir2 = stringify(Dec.output[2]); 1022 std::string Dir3 = stringify(Dec.output[3]); 1023 ASSERT_EQ(Dir0, "target"); // (15) 1024 ASSERT_EQ(Dir1, "parallel if(, expr)"); // (15) 1025 ASSERT_EQ(Dir2, "for"); // (15) 1026 ASSERT_EQ(Dir3, "simd"); // (15) 1027 } 1028 1029 TEST_F(OpenMPDecompositionTest, If2) { 1030 omp::List<omp::Clause> Clauses{ 1031 {OMPC_if, omp::clause::If{{std::nullopt, omp::ExprTy{}}}}, 1032 }; 1033 1034 omp::ConstructDecomposition Dec(AnyVersion, Helper, 1035 OMPD_target_parallel_for_simd, Clauses); 1036 ASSERT_EQ(Dec.output.size(), 4u); 1037 std::string Dir0 = stringify(Dec.output[0]); 1038 std::string Dir1 = stringify(Dec.output[1]); 1039 std::string Dir2 = stringify(Dec.output[2]); 1040 std::string Dir3 = stringify(Dec.output[3]); 1041 ASSERT_EQ(Dir0, "target if(, expr)"); // (16) 1042 ASSERT_EQ(Dir1, "parallel if(, expr)"); // (16) 1043 ASSERT_EQ(Dir2, "for"); // (16) 1044 ASSERT_EQ(Dir3, "simd if(, expr)"); // (16) 1045 } 1046 1047 // LINEAR 1048 // [5.2:118:1-2] 1049 // Directives: declare simd, do, for, simd 1050 // 1051 // [5.2:341:15-22] 1052 // (15.1) The effect of the linear clause is as if it is applied to the 1053 // innermost leaf construct. 1054 // (15.2) Additionally, if the list item is not the iteration variable of a simd 1055 // or worksharing-loop SIMD construct, the effect on the outer leaf constructs 1056 // is as if the list item was specified in firstprivate and lastprivate clauses 1057 // on the combined or composite construct, with the rules specified above 1058 // applied. 1059 // (19) If a list item of the linear clause is the iteration variable of a simd 1060 // or worksharing-loop SIMD construct and it is not declared in the construct, 1061 // the effect on the outer leaf constructs is as if the list item was specified 1062 // in a lastprivate clause on the combined or composite construct with the rules 1063 // specified above applied. 1064 TEST_F(OpenMPDecompositionTest, Linear1) { 1065 omp::Object x{"x"}; 1066 1067 omp::List<omp::Clause> Clauses{ 1068 {OMPC_linear, 1069 omp::clause::Linear{{std::nullopt, std::nullopt, std::nullopt, {x}}}}, 1070 }; 1071 1072 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_for_simd, Clauses); 1073 ASSERT_EQ(Dec.output.size(), 2u); 1074 std::string Dir0 = stringify(Dec.output[0]); 1075 std::string Dir1 = stringify(Dec.output[1]); 1076 ASSERT_EQ(Dir0, "for firstprivate(x) lastprivate(, (x))"); // (15.1), (15.2) 1077 ASSERT_EQ(Dir1, "simd linear(, , , (x)) lastprivate(, (x))"); // (15.1) 1078 } 1079 1080 // NOWAIT 1081 // [5.2:308:11-13] 1082 // Directives: dispatch, do, for, interop, scope, sections, single, target, 1083 // target enter data, target exit data, target update, taskwait, workshare 1084 // 1085 // [5.2:341:23] 1086 // (23) The effect of the nowait clause is as if it is applied to the outermost 1087 // leaf construct that permits it. 1088 TEST_F(OpenMPDecompositionTest, Nowait1) { 1089 omp::List<omp::Clause> Clauses{ 1090 {OMPC_nowait, omp::clause::Nowait{}}, 1091 }; 1092 1093 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_target_parallel_for, 1094 Clauses); 1095 ASSERT_EQ(Dec.output.size(), 3u); 1096 std::string Dir0 = stringify(Dec.output[0]); 1097 std::string Dir1 = stringify(Dec.output[1]); 1098 std::string Dir2 = stringify(Dec.output[2]); 1099 ASSERT_EQ(Dir0, "target nowait"); // (23) 1100 ASSERT_EQ(Dir1, "parallel"); // (23) 1101 ASSERT_EQ(Dir2, "for"); // (23) 1102 } 1103 } // namespace 1104