xref: /llvm-project/flang/lib/Optimizer/OpenACC/FIROpenACCTypeInterfaces.cpp (revision 662133a278f4f3553f061f7999759bae4e842820)
1 //===-- FIROpenACCTypeInterfaces.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 // Implementation of external dialect interfaces for FIR.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "flang/Optimizer/OpenACC/FIROpenACCTypeInterfaces.h"
14 #include "flang/Optimizer/Builder/BoxValue.h"
15 #include "flang/Optimizer/Builder/DirectivesCommon.h"
16 #include "flang/Optimizer/Builder/FIRBuilder.h"
17 #include "flang/Optimizer/Builder/HLFIRTools.h"
18 #include "flang/Optimizer/Dialect/FIROps.h"
19 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
20 #include "flang/Optimizer/Dialect/FIRType.h"
21 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
22 #include "flang/Optimizer/Dialect/Support/KindMapping.h"
23 #include "mlir/Dialect/Arith/IR/Arith.h"
24 #include "mlir/Dialect/OpenACC/OpenACC.h"
25 #include "mlir/IR/BuiltinOps.h"
26 #include "mlir/Support/LLVM.h"
27 
28 namespace fir::acc {
29 
30 static mlir::TypedValue<mlir::acc::PointerLikeType>
31 getPtrFromVar(mlir::Value var) {
32   if (auto ptr =
33           mlir::dyn_cast<mlir::TypedValue<mlir::acc::PointerLikeType>>(var))
34     return ptr;
35 
36   if (auto load = mlir::dyn_cast_if_present<fir::LoadOp>(var.getDefiningOp())) {
37     // All FIR reference types implement the PointerLikeType interface.
38     return mlir::cast<mlir::TypedValue<mlir::acc::PointerLikeType>>(
39         load.getMemref());
40   }
41 
42   return {};
43 }
44 
45 template <>
46 mlir::TypedValue<mlir::acc::PointerLikeType>
47 OpenACCMappableModel<fir::SequenceType>::getVarPtr(mlir::Type type,
48                                                    mlir::Value var) const {
49   return getPtrFromVar(var);
50 }
51 
52 template <>
53 mlir::TypedValue<mlir::acc::PointerLikeType>
54 OpenACCMappableModel<fir::BaseBoxType>::getVarPtr(mlir::Type type,
55                                                   mlir::Value var) const {
56   return getPtrFromVar(var);
57 }
58 
59 template <>
60 std::optional<llvm::TypeSize>
61 OpenACCMappableModel<fir::SequenceType>::getSizeInBytes(
62     mlir::Type type, mlir::Value var, mlir::ValueRange accBounds,
63     const mlir::DataLayout &dataLayout) const {
64   // TODO: Bounds operation affect the total size - add support to take them
65   // into account.
66   if (!accBounds.empty())
67     return {};
68 
69   // Dynamic extents or unknown ranks generally do not have compile-time
70   // computable dimensions.
71   auto seqType = mlir::cast<fir::SequenceType>(type);
72   if (seqType.hasDynamicExtents() || seqType.hasUnknownShape())
73     return {};
74 
75   // Attempt to find an operation that a lookup for KindMapping can be done
76   // from.
77   mlir::Operation *kindMapSrcOp = var.getDefiningOp();
78   if (!kindMapSrcOp) {
79     kindMapSrcOp = var.getParentRegion()->getParentOp();
80     if (!kindMapSrcOp)
81       return {};
82   }
83   auto kindMap = fir::getKindMapping(kindMapSrcOp);
84 
85   auto sizeAndAlignment =
86       fir::getTypeSizeAndAlignment(var.getLoc(), type, dataLayout, kindMap);
87   if (!sizeAndAlignment.has_value())
88     return {};
89 
90   return {llvm::TypeSize::getFixed(sizeAndAlignment->first)};
91 }
92 
93 template <>
94 std::optional<llvm::TypeSize>
95 OpenACCMappableModel<fir::BaseBoxType>::getSizeInBytes(
96     mlir::Type type, mlir::Value var, mlir::ValueRange accBounds,
97     const mlir::DataLayout &dataLayout) const {
98   // If we have a box value instead of box reference, the intent is to
99   // get the size of the data not the box itself.
100   if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(var.getType())) {
101     if (auto mappableTy = mlir::dyn_cast<mlir::acc::MappableType>(
102             fir::unwrapRefType(boxTy.getEleTy()))) {
103       return mappableTy.getSizeInBytes(var, accBounds, dataLayout);
104     }
105   }
106   // Size for boxes is not computable until it gets materialized.
107   return {};
108 }
109 
110 template <>
111 std::optional<int64_t>
112 OpenACCMappableModel<fir::SequenceType>::getOffsetInBytes(
113     mlir::Type type, mlir::Value var, mlir::ValueRange accBounds,
114     const mlir::DataLayout &dataLayout) const {
115   // TODO: Bounds operation affect the offset- add support to take them
116   // into account.
117   if (!accBounds.empty())
118     return {};
119 
120   // Dynamic extents (aka descriptor-based arrays) - may have a offset.
121   // For example, a negative stride may mean a negative offset to compute the
122   // start of array.
123   auto seqType = mlir::cast<fir::SequenceType>(type);
124   if (seqType.hasDynamicExtents() || seqType.hasUnknownShape())
125     return {};
126 
127   // We have non-dynamic extents - but if for some reason the size is not
128   // computable - assume offset is not either. Otherwise, it is an offset of
129   // zero.
130   if (getSizeInBytes(type, var, accBounds, dataLayout).has_value()) {
131     return {0};
132   }
133   return {};
134 }
135 
136 template <>
137 std::optional<int64_t> OpenACCMappableModel<fir::BaseBoxType>::getOffsetInBytes(
138     mlir::Type type, mlir::Value var, mlir::ValueRange accBounds,
139     const mlir::DataLayout &dataLayout) const {
140   // If we have a box value instead of box reference, the intent is to
141   // get the offset of the data not the offset of the box itself.
142   if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(var.getType())) {
143     if (auto mappableTy = mlir::dyn_cast<mlir::acc::MappableType>(
144             fir::unwrapRefType(boxTy.getEleTy()))) {
145       return mappableTy.getOffsetInBytes(var, accBounds, dataLayout);
146     }
147   }
148   // Until boxes get materialized, the offset is not evident because it is
149   // relative to the pointer being held.
150   return {};
151 }
152 
153 template <>
154 llvm::SmallVector<mlir::Value>
155 OpenACCMappableModel<fir::SequenceType>::generateAccBounds(
156     mlir::Type type, mlir::Value var, mlir::OpBuilder &builder) const {
157   assert((mlir::isa<mlir::acc::PointerLikeType>(var.getType()) ||
158           mlir::isa<mlir::acc::MappableType>(var.getType())) &&
159          "must be pointer-like or mappable");
160 
161   fir::FirOpBuilder firBuilder(builder, var.getDefiningOp());
162   auto seqType = mlir::cast<fir::SequenceType>(type);
163   mlir::Location loc = var.getLoc();
164 
165   mlir::Value varPtr =
166       mlir::isa<mlir::acc::PointerLikeType>(var.getType())
167           ? var
168           : mlir::cast<mlir::acc::MappableType>(var.getType()).getVarPtr(var);
169 
170   if (seqType.hasDynamicExtents() || seqType.hasUnknownShape()) {
171     if (auto boxAddr =
172             mlir::dyn_cast_if_present<fir::BoxAddrOp>(varPtr.getDefiningOp())) {
173       mlir::Value box = boxAddr.getVal();
174       auto res =
175           hlfir::translateToExtendedValue(loc, firBuilder, hlfir::Entity(box));
176       fir::ExtendedValue exv = res.first;
177       mlir::Value boxRef = box;
178       if (auto boxPtr = getPtrFromVar(box)) {
179         boxRef = boxPtr;
180       }
181       // TODO: Handle Fortran optional.
182       const mlir::Value isPresent;
183       fir::factory::AddrAndBoundsInfo info(box, boxRef, isPresent,
184                                            box.getType());
185       return fir::factory::genBoundsOpsFromBox<mlir::acc::DataBoundsOp,
186                                                mlir::acc::DataBoundsType>(
187           firBuilder, loc, exv, info);
188     }
189     assert(false && "array with unknown dimension expected to have descriptor");
190     return {};
191   }
192 
193   // TODO: Detect assumed-size case.
194   const bool isAssumedSize = false;
195   auto valToCheck = varPtr;
196   if (auto boxAddr =
197           mlir::dyn_cast_if_present<fir::BoxAddrOp>(varPtr.getDefiningOp())) {
198     valToCheck = boxAddr.getVal();
199   }
200   auto res = hlfir::translateToExtendedValue(loc, firBuilder,
201                                              hlfir::Entity(valToCheck));
202   fir::ExtendedValue exv = res.first;
203   return fir::factory::genBaseBoundsOps<mlir::acc::DataBoundsOp,
204                                         mlir::acc::DataBoundsType>(
205       firBuilder, loc, exv,
206       /*isAssumedSize=*/isAssumedSize);
207 }
208 
209 template <>
210 llvm::SmallVector<mlir::Value>
211 OpenACCMappableModel<fir::BaseBoxType>::generateAccBounds(
212     mlir::Type type, mlir::Value var, mlir::OpBuilder &builder) const {
213   // If we have a box value instead of box reference, the intent is to
214   // get the bounds of the data not the bounds of the box itself.
215   if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(var.getType())) {
216     if (auto mappableTy = mlir::dyn_cast<mlir::acc::MappableType>(
217             fir::unwrapRefType(boxTy.getEleTy()))) {
218       mlir::Value data = builder.create<fir::BoxAddrOp>(var.getLoc(), var);
219       return mappableTy.generateAccBounds(data, builder);
220     }
221   }
222   // Box references are not arrays - thus generating acc.bounds does not make
223   // sense.
224   return {};
225 }
226 
227 } // namespace fir::acc
228