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