| 1 | //===-- Clauses.h -- OpenMP clause handling -------------------------------===// |
| 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 | #ifndef FORTRAN_LOWER_OPENMP_CLAUSES_H |
| 9 | #define FORTRAN_LOWER_OPENMP_CLAUSES_H |
| 10 | |
| 11 | #include "flang/Evaluate/expression.h" |
| 12 | #include "flang/Evaluate/type.h" |
| 13 | #include "flang/Parser/parse-tree.h" |
| 14 | #include "flang/Semantics/expression.h" |
| 15 | #include "flang/Semantics/semantics.h" |
| 16 | #include "flang/Semantics/symbol.h" |
| 17 | |
| 18 | #include "llvm/ADT/STLExtras.h" |
| 19 | #include "llvm/Frontend/OpenMP/ClauseT.h" |
| 20 | #include "llvm/Frontend/OpenMP/OMP.h.inc" |
| 21 | |
| 22 | #include <optional> |
| 23 | #include <type_traits> |
| 24 | #include <utility> |
| 25 | |
| 26 | namespace Fortran::semantics { |
| 27 | class Symbol; |
| 28 | } |
| 29 | |
| 30 | namespace Fortran::lower::omp { |
| 31 | using namespace Fortran; |
| 32 | using SomeExpr = semantics::SomeExpr; |
| 33 | using MaybeExpr = semantics::MaybeExpr; |
| 34 | using TypeTy = evaluate::DynamicType; |
| 35 | |
| 36 | template <typename ExprTy> |
| 37 | struct IdTyTemplate { |
| 38 | // "symbol" is always non-null for id's of actual objects. |
| 39 | Fortran::semantics::Symbol *symbol; |
| 40 | std::optional<ExprTy> designator; |
| 41 | |
| 42 | bool operator==(const IdTyTemplate &other) const { |
| 43 | // If symbols are different, then the objects are different. |
| 44 | if (symbol != other.symbol) |
| 45 | return false; |
| 46 | if (symbol == nullptr) |
| 47 | return true; |
| 48 | // Equal symbols don't necessarily indicate identical objects, |
| 49 | // for example, a derived object component may use a single symbol, |
| 50 | // which will refer to different objects for different designators, |
| 51 | // e.g. a%c and b%c. |
| 52 | return designator == other.designator; |
| 53 | } |
| 54 | |
| 55 | // Defining an "ordering" which allows types derived from this to be |
| 56 | // utilised in maps and other containers that require comparison |
| 57 | // operators for ordering |
| 58 | bool operator<(const IdTyTemplate &other) const { |
| 59 | return symbol < other.symbol; |
| 60 | } |
| 61 | |
| 62 | operator bool() const { return symbol != nullptr; } |
| 63 | }; |
| 64 | |
| 65 | using ExprTy = SomeExpr; |
| 66 | |
| 67 | template <typename T> |
| 68 | using List = tomp::ListT<T>; |
| 69 | } // namespace Fortran::lower::omp |
| 70 | |
| 71 | // Specialization of the ObjectT template |
| 72 | namespace tomp::type { |
| 73 | template <> |
| 74 | struct ObjectT<Fortran::lower::omp::IdTyTemplate<Fortran::lower::omp::ExprTy>, |
| 75 | Fortran::lower::omp::ExprTy> { |
| 76 | using IdTy = Fortran::lower::omp::IdTyTemplate<Fortran::lower::omp::ExprTy>; |
| 77 | using ExprTy = Fortran::lower::omp::ExprTy; |
| 78 | |
| 79 | IdTy id() const { return identity; } |
| 80 | Fortran::semantics::Symbol *sym() const { return identity.symbol; } |
| 81 | const std::optional<ExprTy> &ref() const { return identity.designator; } |
| 82 | |
| 83 | bool operator<(const ObjectT<IdTy, ExprTy> &other) const { |
| 84 | return identity < other.identity; |
| 85 | } |
| 86 | |
| 87 | IdTy identity; |
| 88 | }; |
| 89 | } // namespace tomp::type |
| 90 | |
| 91 | namespace Fortran::lower::omp { |
| 92 | using IdTy = IdTyTemplate<ExprTy>; |
| 93 | } |
| 94 | |
| 95 | namespace std { |
| 96 | template <> |
| 97 | struct hash<Fortran::lower::omp::IdTy> { |
| 98 | size_t operator()(const Fortran::lower::omp::IdTy &id) const { |
| 99 | return static_cast<size_t>(reinterpret_cast<uintptr_t>(id.symbol)); |
| 100 | } |
| 101 | }; |
| 102 | } // namespace std |
| 103 | |
| 104 | namespace Fortran::lower::omp { |
| 105 | using Object = tomp::ObjectT<IdTy, ExprTy>; |
| 106 | using ObjectList = tomp::ObjectListT<IdTy, ExprTy>; |
| 107 | |
| 108 | Object makeObject(const parser::OmpObject &object, |
| 109 | semantics::SemanticsContext &semaCtx); |
| 110 | Object makeObject(const parser::Name &name, |
| 111 | semantics::SemanticsContext &semaCtx); |
| 112 | Object makeObject(const parser::Designator &dsg, |
| 113 | semantics::SemanticsContext &semaCtx); |
| 114 | Object makeObject(const parser::StructureComponent &comp, |
| 115 | semantics::SemanticsContext &semaCtx); |
| 116 | |
| 117 | inline auto makeObjectFn(semantics::SemanticsContext &semaCtx) { |
| 118 | return [&](auto &&s) { return makeObject(s, semaCtx); }; |
| 119 | } |
| 120 | |
| 121 | template <typename T> |
| 122 | SomeExpr makeExpr(T &&pftExpr, semantics::SemanticsContext &semaCtx) { |
| 123 | auto maybeExpr = evaluate::ExpressionAnalyzer(semaCtx).Analyze(pftExpr); |
| 124 | assert(maybeExpr); |
| 125 | return std::move(*maybeExpr); |
| 126 | } |
| 127 | |
| 128 | inline auto makeExprFn(semantics::SemanticsContext &semaCtx) { |
| 129 | return [&](auto &&s) { return makeExpr(s, semaCtx); }; |
| 130 | } |
| 131 | |
| 132 | template < |
| 133 | typename ContainerTy, typename FunctionTy, |
| 134 | typename ElemTy = typename llvm::remove_cvref_t<ContainerTy>::value_type, |
| 135 | typename ResultTy = std::invoke_result_t<FunctionTy, ElemTy>> |
| 136 | List<ResultTy> makeList(ContainerTy &&container, FunctionTy &&func) { |
| 137 | List<ResultTy> v; |
| 138 | llvm::transform(container, std::back_inserter(v), func); |
| 139 | return v; |
| 140 | } |
| 141 | |
| 142 | inline ObjectList makeObjects(const parser::OmpObjectList &objects, |
| 143 | semantics::SemanticsContext &semaCtx) { |
| 144 | return makeList(objects.v, makeObjectFn(semaCtx)); |
| 145 | } |
| 146 | |
| 147 | ObjectList makeObjects(const parser::OmpArgumentList &objects, |
| 148 | semantics::SemanticsContext &semaCtx); |
| 149 | |
| 150 | template <typename FuncTy, // |
| 151 | typename ArgTy, // |
| 152 | typename ResultTy = std::invoke_result_t<FuncTy, ArgTy>> |
| 153 | std::optional<ResultTy> maybeApply(FuncTy &&func, |
| 154 | const std::optional<ArgTy> &arg) { |
| 155 | if (!arg) |
| 156 | return std::nullopt; |
| 157 | return func(*arg); |
| 158 | } |
| 159 | |
| 160 | template < // |
| 161 | typename FuncTy, // |
| 162 | typename ArgTy, // |
| 163 | typename ResultTy = |
| 164 | std::invoke_result_t<FuncTy, decltype(std::declval<ArgTy>().v)>> |
| 165 | std::optional<ResultTy> maybeApplyToV(FuncTy &&func, const ArgTy *arg) { |
| 166 | if (!arg) |
| 167 | return std::nullopt; |
| 168 | return func(arg->v); |
| 169 | } |
| 170 | |
| 171 | std::optional<Object> getBaseObject(const Object &object, |
| 172 | semantics::SemanticsContext &semaCtx); |
| 173 | |
| 174 | namespace clause { |
| 175 | using Range = tomp::type::RangeT<ExprTy>; |
| 176 | using Mapper = tomp::type::MapperT<IdTy, ExprTy>; |
| 177 | using Iterator = tomp::type::IteratorT<TypeTy, IdTy, ExprTy>; |
| 178 | using IteratorSpecifier = tomp::type::IteratorSpecifierT<TypeTy, IdTy, ExprTy>; |
| 179 | using DefinedOperator = tomp::type::DefinedOperatorT<IdTy, ExprTy>; |
| 180 | using ProcedureDesignator = tomp::type::ProcedureDesignatorT<IdTy, ExprTy>; |
| 181 | using ReductionOperator = tomp::type::ReductionIdentifierT<IdTy, ExprTy>; |
| 182 | using DependenceType = tomp::type::DependenceType; |
| 183 | using Prescriptiveness = tomp::type::Prescriptiveness; |
| 184 | |
| 185 | // "Requires" clauses are handled early on, and the aggregated information |
| 186 | // is stored in the Symbol details of modules, programs, and subprograms. |
| 187 | // These clauses are still handled here to cover all alternatives in the |
| 188 | // main clause variant. |
| 189 | |
| 190 | using Absent = tomp::clause::AbsentT<TypeTy, IdTy, ExprTy>; |
| 191 | using AcqRel = tomp::clause::AcqRelT<TypeTy, IdTy, ExprTy>; |
| 192 | using Acquire = tomp::clause::AcquireT<TypeTy, IdTy, ExprTy>; |
| 193 | using AdjustArgs = tomp::clause::AdjustArgsT<TypeTy, IdTy, ExprTy>; |
| 194 | using Affinity = tomp::clause::AffinityT<TypeTy, IdTy, ExprTy>; |
| 195 | using Aligned = tomp::clause::AlignedT<TypeTy, IdTy, ExprTy>; |
| 196 | using Align = tomp::clause::AlignT<TypeTy, IdTy, ExprTy>; |
| 197 | using Allocate = tomp::clause::AllocateT<TypeTy, IdTy, ExprTy>; |
| 198 | using Allocator = tomp::clause::AllocatorT<TypeTy, IdTy, ExprTy>; |
| 199 | using AppendArgs = tomp::clause::AppendArgsT<TypeTy, IdTy, ExprTy>; |
| 200 | using AtomicDefaultMemOrder = |
| 201 | tomp::clause::AtomicDefaultMemOrderT<TypeTy, IdTy, ExprTy>; |
| 202 | using At = tomp::clause::AtT<TypeTy, IdTy, ExprTy>; |
| 203 | using Bind = tomp::clause::BindT<TypeTy, IdTy, ExprTy>; |
| 204 | using Capture = tomp::clause::CaptureT<TypeTy, IdTy, ExprTy>; |
| 205 | using Collapse = tomp::clause::CollapseT<TypeTy, IdTy, ExprTy>; |
| 206 | using Compare = tomp::clause::CompareT<TypeTy, IdTy, ExprTy>; |
| 207 | using Contains = tomp::clause::ContainsT<TypeTy, IdTy, ExprTy>; |
| 208 | using Copyin = tomp::clause::CopyinT<TypeTy, IdTy, ExprTy>; |
| 209 | using Copyprivate = tomp::clause::CopyprivateT<TypeTy, IdTy, ExprTy>; |
| 210 | using Defaultmap = tomp::clause::DefaultmapT<TypeTy, IdTy, ExprTy>; |
| 211 | using Default = tomp::clause::DefaultT<TypeTy, IdTy, ExprTy>; |
| 212 | using Depend = tomp::clause::DependT<TypeTy, IdTy, ExprTy>; |
| 213 | using Destroy = tomp::clause::DestroyT<TypeTy, IdTy, ExprTy>; |
| 214 | using Detach = tomp::clause::DetachT<TypeTy, IdTy, ExprTy>; |
| 215 | using Device = tomp::clause::DeviceT<TypeTy, IdTy, ExprTy>; |
| 216 | using DeviceType = tomp::clause::DeviceTypeT<TypeTy, IdTy, ExprTy>; |
| 217 | using DistSchedule = tomp::clause::DistScheduleT<TypeTy, IdTy, ExprTy>; |
| 218 | using Doacross = tomp::clause::DoacrossT<TypeTy, IdTy, ExprTy>; |
| 219 | using DynamicAllocators = |
| 220 | tomp::clause::DynamicAllocatorsT<TypeTy, IdTy, ExprTy>; |
| 221 | using Enter = tomp::clause::EnterT<TypeTy, IdTy, ExprTy>; |
| 222 | using Exclusive = tomp::clause::ExclusiveT<TypeTy, IdTy, ExprTy>; |
| 223 | using Fail = tomp::clause::FailT<TypeTy, IdTy, ExprTy>; |
| 224 | using Filter = tomp::clause::FilterT<TypeTy, IdTy, ExprTy>; |
| 225 | using Final = tomp::clause::FinalT<TypeTy, IdTy, ExprTy>; |
| 226 | using Firstprivate = tomp::clause::FirstprivateT<TypeTy, IdTy, ExprTy>; |
| 227 | using From = tomp::clause::FromT<TypeTy, IdTy, ExprTy>; |
| 228 | using Full = tomp::clause::FullT<TypeTy, IdTy, ExprTy>; |
| 229 | using Grainsize = tomp::clause::GrainsizeT<TypeTy, IdTy, ExprTy>; |
| 230 | using HasDeviceAddr = tomp::clause::HasDeviceAddrT<TypeTy, IdTy, ExprTy>; |
| 231 | using Hint = tomp::clause::HintT<TypeTy, IdTy, ExprTy>; |
| 232 | using Holds = tomp::clause::HoldsT<TypeTy, IdTy, ExprTy>; |
| 233 | using If = tomp::clause::IfT<TypeTy, IdTy, ExprTy>; |
| 234 | using Inbranch = tomp::clause::InbranchT<TypeTy, IdTy, ExprTy>; |
| 235 | using Inclusive = tomp::clause::InclusiveT<TypeTy, IdTy, ExprTy>; |
| 236 | using Indirect = tomp::clause::IndirectT<TypeTy, IdTy, ExprTy>; |
| 237 | using Init = tomp::clause::InitT<TypeTy, IdTy, ExprTy>; |
| 238 | using Initializer = tomp::clause::InitializerT<TypeTy, IdTy, ExprTy>; |
| 239 | using InReduction = tomp::clause::InReductionT<TypeTy, IdTy, ExprTy>; |
| 240 | using IsDevicePtr = tomp::clause::IsDevicePtrT<TypeTy, IdTy, ExprTy>; |
| 241 | using Lastprivate = tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy>; |
| 242 | using Linear = tomp::clause::LinearT<TypeTy, IdTy, ExprTy>; |
| 243 | using Link = tomp::clause::LinkT<TypeTy, IdTy, ExprTy>; |
| 244 | using Map = tomp::clause::MapT<TypeTy, IdTy, ExprTy>; |
| 245 | using Match = tomp::clause::MatchT<TypeTy, IdTy, ExprTy>; |
| 246 | using Mergeable = tomp::clause::MergeableT<TypeTy, IdTy, ExprTy>; |
| 247 | using Message = tomp::clause::MessageT<TypeTy, IdTy, ExprTy>; |
| 248 | using NoOpenmp = tomp::clause::NoOpenmpT<TypeTy, IdTy, ExprTy>; |
| 249 | using NoOpenmpRoutines = tomp::clause::NoOpenmpRoutinesT<TypeTy, IdTy, ExprTy>; |
| 250 | using NoOpenmpConstructs = |
| 251 | tomp::clause::NoOpenmpConstructsT<TypeTy, IdTy, ExprTy>; |
| 252 | using NoParallelism = tomp::clause::NoParallelismT<TypeTy, IdTy, ExprTy>; |
| 253 | using Nocontext = tomp::clause::NocontextT<TypeTy, IdTy, ExprTy>; |
| 254 | using Nogroup = tomp::clause::NogroupT<TypeTy, IdTy, ExprTy>; |
| 255 | using Nontemporal = tomp::clause::NontemporalT<TypeTy, IdTy, ExprTy>; |
| 256 | using Notinbranch = tomp::clause::NotinbranchT<TypeTy, IdTy, ExprTy>; |
| 257 | using Novariants = tomp::clause::NovariantsT<TypeTy, IdTy, ExprTy>; |
| 258 | using Nowait = tomp::clause::NowaitT<TypeTy, IdTy, ExprTy>; |
| 259 | using NumTasks = tomp::clause::NumTasksT<TypeTy, IdTy, ExprTy>; |
| 260 | using NumTeams = tomp::clause::NumTeamsT<TypeTy, IdTy, ExprTy>; |
| 261 | using NumThreads = tomp::clause::NumThreadsT<TypeTy, IdTy, ExprTy>; |
| 262 | using OmpxAttribute = tomp::clause::OmpxAttributeT<TypeTy, IdTy, ExprTy>; |
| 263 | using OmpxBare = tomp::clause::OmpxBareT<TypeTy, IdTy, ExprTy>; |
| 264 | using OmpxDynCgroupMem = tomp::clause::OmpxDynCgroupMemT<TypeTy, IdTy, ExprTy>; |
| 265 | using Ordered = tomp::clause::OrderedT<TypeTy, IdTy, ExprTy>; |
| 266 | using Order = tomp::clause::OrderT<TypeTy, IdTy, ExprTy>; |
| 267 | using Otherwise = tomp::clause::OtherwiseT<TypeTy, IdTy, ExprTy>; |
| 268 | using Partial = tomp::clause::PartialT<TypeTy, IdTy, ExprTy>; |
| 269 | using Priority = tomp::clause::PriorityT<TypeTy, IdTy, ExprTy>; |
| 270 | using Private = tomp::clause::PrivateT<TypeTy, IdTy, ExprTy>; |
| 271 | using ProcBind = tomp::clause::ProcBindT<TypeTy, IdTy, ExprTy>; |
| 272 | using Read = tomp::clause::ReadT<TypeTy, IdTy, ExprTy>; |
| 273 | using Reduction = tomp::clause::ReductionT<TypeTy, IdTy, ExprTy>; |
| 274 | using Relaxed = tomp::clause::RelaxedT<TypeTy, IdTy, ExprTy>; |
| 275 | using Release = tomp::clause::ReleaseT<TypeTy, IdTy, ExprTy>; |
| 276 | using ReverseOffload = tomp::clause::ReverseOffloadT<TypeTy, IdTy, ExprTy>; |
| 277 | using Safelen = tomp::clause::SafelenT<TypeTy, IdTy, ExprTy>; |
| 278 | using Schedule = tomp::clause::ScheduleT<TypeTy, IdTy, ExprTy>; |
| 279 | using SeqCst = tomp::clause::SeqCstT<TypeTy, IdTy, ExprTy>; |
| 280 | using Severity = tomp::clause::SeverityT<TypeTy, IdTy, ExprTy>; |
| 281 | using Shared = tomp::clause::SharedT<TypeTy, IdTy, ExprTy>; |
| 282 | using Simdlen = tomp::clause::SimdlenT<TypeTy, IdTy, ExprTy>; |
| 283 | using Simd = tomp::clause::SimdT<TypeTy, IdTy, ExprTy>; |
| 284 | using Sizes = tomp::clause::SizesT<TypeTy, IdTy, ExprTy>; |
| 285 | using Permutation = tomp::clause::PermutationT<TypeTy, IdTy, ExprTy>; |
| 286 | using TaskReduction = tomp::clause::TaskReductionT<TypeTy, IdTy, ExprTy>; |
| 287 | using ThreadLimit = tomp::clause::ThreadLimitT<TypeTy, IdTy, ExprTy>; |
| 288 | using Threads = tomp::clause::ThreadsT<TypeTy, IdTy, ExprTy>; |
| 289 | using To = tomp::clause::ToT<TypeTy, IdTy, ExprTy>; |
| 290 | using UnifiedAddress = tomp::clause::UnifiedAddressT<TypeTy, IdTy, ExprTy>; |
| 291 | using UnifiedSharedMemory = |
| 292 | tomp::clause::UnifiedSharedMemoryT<TypeTy, IdTy, ExprTy>; |
| 293 | using SelfMaps = tomp::clause::SelfMapsT<TypeTy, IdTy, ExprTy>; |
| 294 | using Uniform = tomp::clause::UniformT<TypeTy, IdTy, ExprTy>; |
| 295 | using Unknown = tomp::clause::UnknownT<TypeTy, IdTy, ExprTy>; |
| 296 | using Untied = tomp::clause::UntiedT<TypeTy, IdTy, ExprTy>; |
| 297 | using Update = tomp::clause::UpdateT<TypeTy, IdTy, ExprTy>; |
| 298 | using UseDeviceAddr = tomp::clause::UseDeviceAddrT<TypeTy, IdTy, ExprTy>; |
| 299 | using UseDevicePtr = tomp::clause::UseDevicePtrT<TypeTy, IdTy, ExprTy>; |
| 300 | using UsesAllocators = tomp::clause::UsesAllocatorsT<TypeTy, IdTy, ExprTy>; |
| 301 | using Use = tomp::clause::UseT<TypeTy, IdTy, ExprTy>; |
| 302 | using Weak = tomp::clause::WeakT<TypeTy, IdTy, ExprTy>; |
| 303 | using When = tomp::clause::WhenT<TypeTy, IdTy, ExprTy>; |
| 304 | using Write = tomp::clause::WriteT<TypeTy, IdTy, ExprTy>; |
| 305 | } // namespace clause |
| 306 | |
| 307 | using tomp::type::operator==; |
| 308 | |
| 309 | struct CancellationConstructType { |
| 310 | using WrapperTrait = std::true_type; |
| 311 | llvm::omp::CancellationConstructType v; |
| 312 | }; |
| 313 | struct Depobj { |
| 314 | using EmptyTrait = std::true_type; |
| 315 | }; |
| 316 | struct Flush { |
| 317 | using EmptyTrait = std::true_type; |
| 318 | }; |
| 319 | struct MemoryOrder { |
| 320 | using EmptyTrait = std::true_type; |
| 321 | }; |
| 322 | struct Threadprivate { |
| 323 | using EmptyTrait = std::true_type; |
| 324 | }; |
| 325 | |
| 326 | using ClauseBase = tomp::ClauseT<TypeTy, IdTy, ExprTy, |
| 327 | // Extras... |
| 328 | CancellationConstructType, Depobj, Flush, |
| 329 | MemoryOrder, Threadprivate>; |
| 330 | |
| 331 | struct Clause : public ClauseBase { |
| 332 | Clause(ClauseBase &&base, const parser::CharBlock source = {}) |
| 333 | : ClauseBase(std::move(base)), source(source) {} |
| 334 | // "source" will be ignored by tomp::type::operator==. |
| 335 | parser::CharBlock source; |
| 336 | }; |
| 337 | |
| 338 | template <typename Specific> |
| 339 | Clause makeClause(llvm::omp::Clause id, Specific &&specific, |
| 340 | parser::CharBlock source = {}) { |
| 341 | return Clause(typename Clause::BaseT{id, specific}, source); |
| 342 | } |
| 343 | |
| 344 | Clause makeClause(const parser::OmpClause &cls, |
| 345 | semantics::SemanticsContext &semaCtx); |
| 346 | |
| 347 | List<Clause> makeClauses(const parser::OmpClauseList &clauses, |
| 348 | semantics::SemanticsContext &semaCtx); |
| 349 | |
| 350 | bool transferLocations(const List<Clause> &from, List<Clause> &to); |
| 351 | } // namespace Fortran::lower::omp |
| 352 | |
| 353 | #endif // FORTRAN_LOWER_OPENMP_CLAUSES_H |
| 354 | |