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