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, omp::clause::Allocate{{std::nullopt, std::nullopt, {x}}}}, 712 {OMPC_firstprivate, omp::clause::Firstprivate{{x}}}, 713 }; 714 715 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections, 716 Clauses); 717 ASSERT_EQ(Dec.output.size(), 2u); 718 719 std::string Dir0 = stringify(Dec.output[0]); 720 std::string Dir1 = stringify(Dec.output[1]); 721 ASSERT_EQ(Dir0, "parallel shared(x)"); // (33) 722 ASSERT_EQ(Dir1, "sections firstprivate(x) allocate(, , (x))"); // (33) 723 } 724 725 TEST_F(OpenMPDecompositionTest, Allocate2) { 726 omp::Object x{"x"}; 727 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 728 729 // Allocate + in_reduction 730 omp::List<omp::Clause> Clauses{ 731 {OMPC_allocate, omp::clause::Allocate{{std::nullopt, std::nullopt, {x}}}}, 732 {OMPC_in_reduction, omp::clause::InReduction{{{Add}, {x}}}}, 733 }; 734 735 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_target_parallel, 736 Clauses); 737 ASSERT_EQ(Dec.output.size(), 2u); 738 739 std::string Dir0 = stringify(Dec.output[0]); 740 std::string Dir1 = stringify(Dec.output[1]); 741 ASSERT_EQ(Dir0, "target in_reduction((3), (x)) allocate(, , (x))"); // (33) 742 ASSERT_EQ(Dir1, "parallel"); // (33) 743 } 744 745 TEST_F(OpenMPDecompositionTest, Allocate3) { 746 omp::Object x{"x"}; 747 748 // Allocate + linear 749 omp::List<omp::Clause> Clauses{ 750 {OMPC_allocate, omp::clause::Allocate{{std::nullopt, std::nullopt, {x}}}}, 751 {OMPC_linear, omp::clause::Linear{{std::nullopt, std::nullopt, {x}}}}, 752 }; 753 754 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_for, 755 Clauses); 756 ASSERT_EQ(Dec.output.size(), 2u); 757 758 std::string Dir0 = stringify(Dec.output[0]); 759 std::string Dir1 = stringify(Dec.output[1]); 760 // The "shared" clause is duplicated---this isn't harmful, but it 761 // should be fixed eventually. 762 ASSERT_EQ(Dir0, "parallel shared(x) shared(x)"); // (33) 763 ASSERT_EQ(Dir1, "for linear(, , (x)) firstprivate(x) lastprivate(, (x)) " 764 "allocate(, , (x))"); // (33) 765 } 766 767 TEST_F(OpenMPDecompositionTest, Allocate4) { 768 omp::Object x{"x"}; 769 770 // Allocate + lastprivate 771 omp::List<omp::Clause> Clauses{ 772 {OMPC_allocate, omp::clause::Allocate{{std::nullopt, std::nullopt, {x}}}}, 773 {OMPC_lastprivate, omp::clause::Lastprivate{{std::nullopt, {x}}}}, 774 }; 775 776 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections, 777 Clauses); 778 ASSERT_EQ(Dec.output.size(), 2u); 779 780 std::string Dir0 = stringify(Dec.output[0]); 781 std::string Dir1 = stringify(Dec.output[1]); 782 ASSERT_EQ(Dir0, "parallel shared(x)"); // (33) 783 ASSERT_EQ(Dir1, "sections lastprivate(, (x)) allocate(, , (x))"); // (33) 784 } 785 786 TEST_F(OpenMPDecompositionTest, Allocate5) { 787 omp::Object x{"x"}; 788 789 // Allocate + private 790 omp::List<omp::Clause> Clauses{ 791 {OMPC_allocate, omp::clause::Allocate{{std::nullopt, std::nullopt, {x}}}}, 792 {OMPC_private, omp::clause::Private{{x}}}, 793 }; 794 795 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections, 796 Clauses); 797 ASSERT_EQ(Dec.output.size(), 2u); 798 799 std::string Dir0 = stringify(Dec.output[0]); 800 std::string Dir1 = stringify(Dec.output[1]); 801 ASSERT_EQ(Dir0, "parallel"); // (33) 802 ASSERT_EQ(Dir1, "sections private(x) allocate(, , (x))"); // (33) 803 } 804 805 TEST_F(OpenMPDecompositionTest, Allocate6) { 806 omp::Object x{"x"}; 807 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 808 809 // Allocate + reduction 810 omp::List<omp::Clause> Clauses{ 811 {OMPC_allocate, omp::clause::Allocate{{std::nullopt, std::nullopt, {x}}}}, 812 {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}}, 813 }; 814 815 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections, 816 Clauses); 817 ASSERT_EQ(Dec.output.size(), 2u); 818 819 std::string Dir0 = stringify(Dec.output[0]); 820 std::string Dir1 = stringify(Dec.output[1]); 821 ASSERT_EQ(Dir0, "parallel shared(x)"); // (33) 822 ASSERT_EQ(Dir1, "sections reduction(, (3), (x)) allocate(, , (x))"); // (33) 823 } 824 825 // REDUCTION 826 // [5.2:134:17-18] 827 // Directives: do, for, loop, parallel, scope, sections, simd, taskloop, teams 828 // 829 // [5.2:340-341:36-13] 830 // (36) The effect of the reduction clause is as if it is applied to all leaf 831 // constructs that permit the clause, except for the following constructs: 832 // (1) The parallel construct, when combined with the sections, 833 // worksharing-loop, loop, or taskloop construct; and 834 // (3) The teams construct, when combined with the loop construct. 835 // (4) For the parallel and teams constructs above, the effect of the reduction 836 // clause instead is as if each list item or, for any list item that is an array 837 // item, its corresponding base array or base pointer appears in a shared clause 838 // for the construct. 839 // (6) If the task reduction-modifier is specified, the effect is as if it only 840 // modifies the behavior of the reduction clause on the innermost leaf construct 841 // that accepts the modifier (see Section 5.5.8). 842 // (8) If the inscan reduction-modifier is specified, the effect is as if it 843 // modifies the behavior of the reduction clause on all constructs of the 844 // combined construct to which the clause is applied and that accept the 845 // modifier. 846 // (10) If a list item in a reduction clause on a combined target construct does 847 // not have the same base variable or base pointer as a list item in a map 848 // clause on the construct, then the effect is as if the list item in the 849 // reduction clause appears as a list item in a map clause with a map-type of 850 // tofrom. 851 TEST_F(OpenMPDecompositionTest, Reduction1) { 852 omp::Object x{"x"}; 853 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 854 855 omp::List<omp::Clause> Clauses{ 856 {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}}, 857 }; 858 859 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_sections, 860 Clauses); 861 ASSERT_EQ(Dec.output.size(), 2u); 862 863 std::string Dir0 = stringify(Dec.output[0]); 864 std::string Dir1 = stringify(Dec.output[1]); 865 ASSERT_EQ(Dir0, "parallel shared(x)"); // (36), (1), (4) 866 ASSERT_EQ(Dir1, "sections reduction(, (3), (x))"); // (36) 867 } 868 869 TEST_F(OpenMPDecompositionTest, Reduction2) { 870 omp::Object x{"x"}; 871 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 872 873 omp::List<omp::Clause> Clauses{ 874 {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}}, 875 }; 876 877 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_parallel_masked, 878 Clauses); 879 ASSERT_EQ(Dec.output.size(), 2u); 880 881 std::string Dir0 = stringify(Dec.output[0]); 882 std::string Dir1 = stringify(Dec.output[1]); 883 ASSERT_EQ(Dir0, "parallel reduction(, (3), (x))"); // (36), (1), (4) 884 ASSERT_EQ(Dir1, "masked"); // (36) 885 } 886 887 TEST_F(OpenMPDecompositionTest, Reduction3) { 888 omp::Object x{"x"}; 889 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 890 891 omp::List<omp::Clause> Clauses{ 892 {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}}, 893 }; 894 895 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_teams_loop, Clauses); 896 ASSERT_EQ(Dec.output.size(), 2u); 897 898 std::string Dir0 = stringify(Dec.output[0]); 899 std::string Dir1 = stringify(Dec.output[1]); 900 ASSERT_EQ(Dir0, "teams shared(x)"); // (36), (3), (4) 901 ASSERT_EQ(Dir1, "loop reduction(, (3), (x))"); // (36) 902 } 903 904 TEST_F(OpenMPDecompositionTest, Reduction4) { 905 omp::Object x{"x"}; 906 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 907 908 omp::List<omp::Clause> Clauses{ 909 {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}}, 910 }; 911 912 omp::ConstructDecomposition Dec(AnyVersion, Helper, 913 OMPD_teams_distribute_parallel_for, Clauses); 914 ASSERT_EQ(Dec.output.size(), 4u); 915 916 std::string Dir0 = stringify(Dec.output[0]); 917 std::string Dir1 = stringify(Dec.output[1]); 918 std::string Dir2 = stringify(Dec.output[2]); 919 std::string Dir3 = stringify(Dec.output[3]); 920 ASSERT_EQ(Dir0, "teams reduction(, (3), (x))"); // (36), (3) 921 ASSERT_EQ(Dir1, "distribute"); // (36) 922 ASSERT_EQ(Dir2, "parallel shared(x)"); // (36), (1), (4) 923 ASSERT_EQ(Dir3, "for reduction(, (3), (x))"); // (36) 924 } 925 926 TEST_F(OpenMPDecompositionTest, Reduction5) { 927 omp::Object x{"x"}; 928 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 929 auto TaskMod = omp::clause::Reduction::ReductionModifier::Task; 930 931 omp::List<omp::Clause> Clauses{ 932 {OMPC_reduction, omp::clause::Reduction{{TaskMod, {Add}, {x}}}}, 933 }; 934 935 omp::ConstructDecomposition Dec(AnyVersion, Helper, 936 OMPD_teams_distribute_parallel_for, Clauses); 937 ASSERT_EQ(Dec.output.size(), 4u); 938 939 std::string Dir0 = stringify(Dec.output[0]); 940 std::string Dir1 = stringify(Dec.output[1]); 941 std::string Dir2 = stringify(Dec.output[2]); 942 std::string Dir3 = stringify(Dec.output[3]); 943 ASSERT_EQ(Dir0, "teams reduction(, (3), (x))"); // (36), (3), (6) 944 ASSERT_EQ(Dir1, "distribute"); // (36) 945 ASSERT_EQ(Dir2, "parallel shared(x)"); // (36), (1), (4) 946 ASSERT_EQ(Dir3, "for reduction(2, (3), (x))"); // (36), (6) 947 } 948 949 TEST_F(OpenMPDecompositionTest, Reduction6) { 950 omp::Object x{"x"}; 951 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 952 auto InscanMod = omp::clause::Reduction::ReductionModifier::Inscan; 953 954 omp::List<omp::Clause> Clauses{ 955 {OMPC_reduction, omp::clause::Reduction{{InscanMod, {Add}, {x}}}}, 956 }; 957 958 omp::ConstructDecomposition Dec(AnyVersion, Helper, 959 OMPD_teams_distribute_parallel_for, Clauses); 960 ASSERT_EQ(Dec.output.size(), 4u); 961 962 std::string Dir0 = stringify(Dec.output[0]); 963 std::string Dir1 = stringify(Dec.output[1]); 964 std::string Dir2 = stringify(Dec.output[2]); 965 std::string Dir3 = stringify(Dec.output[3]); 966 ASSERT_EQ(Dir0, "teams reduction(, (3), (x))"); // (36), (3), (8) 967 ASSERT_EQ(Dir1, "distribute"); // (36) 968 ASSERT_EQ(Dir2, "parallel shared(x)"); // (36), (1), (4) 969 ASSERT_EQ(Dir3, "for reduction(1, (3), (x))"); // (36), (8) 970 } 971 972 TEST_F(OpenMPDecompositionTest, Reduction7) { 973 omp::Object x{"x"}; 974 auto Add = red::makeOp(omp::clause::DefinedOperator::IntrinsicOperator::Add); 975 976 omp::List<omp::Clause> Clauses{ 977 {OMPC_reduction, omp::clause::Reduction{{std::nullopt, {Add}, {x}}}}, 978 }; 979 980 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_target_parallel_do, 981 Clauses); 982 ASSERT_EQ(Dec.output.size(), 3u); 983 984 std::string Dir0 = stringify(Dec.output[0]); 985 std::string Dir1 = stringify(Dec.output[1]); 986 std::string Dir2 = stringify(Dec.output[2]); 987 ASSERT_EQ(Dir0, "target map(2, , , , (x))"); // (36), (10) 988 ASSERT_EQ(Dir1, "parallel shared(x)"); // (36), (1), (4) 989 ASSERT_EQ(Dir2, "do reduction(, (3), (x))"); // (36) 990 } 991 992 // IF 993 // [5.2:72:7-9] 994 // Directives: cancel, parallel, simd, target, target data, target enter data, 995 // target exit data, target update, task, taskloop 996 // 997 // [5.2:72:15-18] 998 // (15) For combined or composite constructs, the if clause only applies to the 999 // semantics of the construct named in the directive-name-modifier. 1000 // (16) For a combined or composite construct, if no directive-name-modifier is 1001 // specified then the if clause applies to all constituent constructs to which 1002 // an if clause can apply. 1003 TEST_F(OpenMPDecompositionTest, If1) { 1004 omp::List<omp::Clause> Clauses{ 1005 {OMPC_if, 1006 omp::clause::If{{llvm::omp::Directive::OMPD_parallel, omp::ExprTy{}}}}, 1007 }; 1008 1009 omp::ConstructDecomposition Dec(AnyVersion, Helper, 1010 OMPD_target_parallel_for_simd, Clauses); 1011 ASSERT_EQ(Dec.output.size(), 4u); 1012 std::string Dir0 = stringify(Dec.output[0]); 1013 std::string Dir1 = stringify(Dec.output[1]); 1014 std::string Dir2 = stringify(Dec.output[2]); 1015 std::string Dir3 = stringify(Dec.output[3]); 1016 ASSERT_EQ(Dir0, "target"); // (15) 1017 ASSERT_EQ(Dir1, "parallel if(, expr)"); // (15) 1018 ASSERT_EQ(Dir2, "for"); // (15) 1019 ASSERT_EQ(Dir3, "simd"); // (15) 1020 } 1021 1022 TEST_F(OpenMPDecompositionTest, If2) { 1023 omp::List<omp::Clause> Clauses{ 1024 {OMPC_if, omp::clause::If{{std::nullopt, omp::ExprTy{}}}}, 1025 }; 1026 1027 omp::ConstructDecomposition Dec(AnyVersion, Helper, 1028 OMPD_target_parallel_for_simd, Clauses); 1029 ASSERT_EQ(Dec.output.size(), 4u); 1030 std::string Dir0 = stringify(Dec.output[0]); 1031 std::string Dir1 = stringify(Dec.output[1]); 1032 std::string Dir2 = stringify(Dec.output[2]); 1033 std::string Dir3 = stringify(Dec.output[3]); 1034 ASSERT_EQ(Dir0, "target if(, expr)"); // (16) 1035 ASSERT_EQ(Dir1, "parallel if(, expr)"); // (16) 1036 ASSERT_EQ(Dir2, "for"); // (16) 1037 ASSERT_EQ(Dir3, "simd if(, expr)"); // (16) 1038 } 1039 1040 // LINEAR 1041 // [5.2:118:1-2] 1042 // Directives: declare simd, do, for, simd 1043 // 1044 // [5.2:341:15-22] 1045 // (15.1) The effect of the linear clause is as if it is applied to the 1046 // innermost leaf construct. 1047 // (15.2) Additionally, if the list item is not the iteration variable of a simd 1048 // or worksharing-loop SIMD construct, the effect on the outer leaf constructs 1049 // is as if the list item was specified in firstprivate and lastprivate clauses 1050 // on the combined or composite construct, with the rules specified above 1051 // applied. 1052 // (19) If a list item of the linear clause is the iteration variable of a simd 1053 // or worksharing-loop SIMD construct and it is not declared in the construct, 1054 // the effect on the outer leaf constructs is as if the list item was specified 1055 // in a lastprivate clause on the combined or composite construct with the rules 1056 // specified above applied. 1057 TEST_F(OpenMPDecompositionTest, Linear1) { 1058 omp::Object x{"x"}; 1059 1060 omp::List<omp::Clause> Clauses{ 1061 {OMPC_linear, omp::clause::Linear{{std::nullopt, std::nullopt, {x}}}}, 1062 }; 1063 1064 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_for_simd, Clauses); 1065 ASSERT_EQ(Dec.output.size(), 2u); 1066 std::string Dir0 = stringify(Dec.output[0]); 1067 std::string Dir1 = stringify(Dec.output[1]); 1068 ASSERT_EQ(Dir0, "for firstprivate(x) lastprivate(, (x))"); // (15.1), (15.2) 1069 ASSERT_EQ(Dir1, "simd linear(, , (x)) lastprivate(, (x))"); // (15.1) 1070 } 1071 1072 // NOWAIT 1073 // [5.2:308:11-13] 1074 // Directives: dispatch, do, for, interop, scope, sections, single, target, 1075 // target enter data, target exit data, target update, taskwait, workshare 1076 // 1077 // [5.2:341:23] 1078 // (23) The effect of the nowait clause is as if it is applied to the outermost 1079 // leaf construct that permits it. 1080 TEST_F(OpenMPDecompositionTest, Nowait1) { 1081 omp::List<omp::Clause> Clauses{ 1082 {OMPC_nowait, omp::clause::Nowait{}}, 1083 }; 1084 1085 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_target_parallel_for, 1086 Clauses); 1087 ASSERT_EQ(Dec.output.size(), 3u); 1088 std::string Dir0 = stringify(Dec.output[0]); 1089 std::string Dir1 = stringify(Dec.output[1]); 1090 std::string Dir2 = stringify(Dec.output[2]); 1091 ASSERT_EQ(Dir0, "target nowait"); // (23) 1092 ASSERT_EQ(Dir1, "parallel"); // (23) 1093 ASSERT_EQ(Dir2, "for"); // (23) 1094 } 1095 1096 // --- 1097 1098 // Check that "simd linear(x)" does not fail despite the implied "firstprivate" 1099 // (which "simd" does not allow). 1100 TEST_F(OpenMPDecompositionTest, Misc1) { 1101 omp::Object x{"x"}; 1102 omp::List<omp::Clause> Clauses{ 1103 {OMPC_linear, omp::clause::Linear{{std::nullopt, std::nullopt, {x}}}}, 1104 }; 1105 1106 omp::ConstructDecomposition Dec(AnyVersion, Helper, OMPD_simd, Clauses); 1107 ASSERT_EQ(Dec.output.size(), 1u); 1108 std::string Dir0 = stringify(Dec.output[0]); 1109 ASSERT_EQ(Dir0, "simd linear(, , (x)) lastprivate(, (x))"); 1110 } 1111 } // namespace 1112