1 //===-- CodeGen.cpp -- bridge to lower to LLVM ----------------------------===// 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 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "flang/Optimizer/CodeGen/FIROpPatterns.h" 14 #include "mlir/Dialect/OpenMP/OpenMPDialect.h" 15 #include "llvm/Support/Debug.h" 16 17 static inline mlir::Type getLlvmPtrType(mlir::MLIRContext *context, 18 unsigned addressSpace = 0) { 19 return mlir::LLVM::LLVMPointerType::get(context, addressSpace); 20 } 21 22 static unsigned getTypeDescFieldId(mlir::Type ty) { 23 auto isArray = mlir::isa<fir::SequenceType>(fir::dyn_cast_ptrOrBoxEleTy(ty)); 24 return isArray ? kOptTypePtrPosInBox : kDimsPosInBox; 25 } 26 27 namespace fir { 28 29 ConvertFIRToLLVMPattern::ConvertFIRToLLVMPattern( 30 llvm::StringRef rootOpName, mlir::MLIRContext *context, 31 const fir::LLVMTypeConverter &typeConverter, 32 const fir::FIRToLLVMPassOptions &options, mlir::PatternBenefit benefit) 33 : ConvertToLLVMPattern(rootOpName, context, typeConverter, benefit), 34 options(options) {} 35 36 // Convert FIR type to LLVM without turning fir.box<T> into memory 37 // reference. 38 mlir::Type 39 ConvertFIRToLLVMPattern::convertObjectType(mlir::Type firType) const { 40 if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(firType)) 41 return lowerTy().convertBoxTypeAsStruct(boxTy); 42 return lowerTy().convertType(firType); 43 } 44 45 mlir::LLVM::ConstantOp ConvertFIRToLLVMPattern::genI32Constant( 46 mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, 47 int value) const { 48 mlir::Type i32Ty = rewriter.getI32Type(); 49 mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value); 50 return rewriter.create<mlir::LLVM::ConstantOp>(loc, i32Ty, attr); 51 } 52 53 mlir::LLVM::ConstantOp ConvertFIRToLLVMPattern::genConstantOffset( 54 mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, 55 int offset) const { 56 mlir::Type ity = lowerTy().offsetType(); 57 mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset); 58 return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr); 59 } 60 61 /// Perform an extension or truncation as needed on an integer value. Lowering 62 /// to the specific target may involve some sign-extending or truncation of 63 /// values, particularly to fit them from abstract box types to the 64 /// appropriate reified structures. 65 mlir::Value 66 ConvertFIRToLLVMPattern::integerCast(mlir::Location loc, 67 mlir::ConversionPatternRewriter &rewriter, 68 mlir::Type ty, mlir::Value val) const { 69 auto valTy = val.getType(); 70 // If the value was not yet lowered, lower its type so that it can 71 // be used in getPrimitiveTypeSizeInBits. 72 if (!mlir::isa<mlir::IntegerType>(valTy)) 73 valTy = convertType(valTy); 74 auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 75 auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy); 76 if (toSize < fromSize) 77 return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val); 78 if (toSize > fromSize) 79 return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val); 80 return val; 81 } 82 83 fir::ConvertFIRToLLVMPattern::TypePair 84 ConvertFIRToLLVMPattern::getBoxTypePair(mlir::Type firBoxTy) const { 85 mlir::Type llvmBoxTy = 86 lowerTy().convertBoxTypeAsStruct(mlir::cast<fir::BaseBoxType>(firBoxTy)); 87 return TypePair{firBoxTy, llvmBoxTy}; 88 } 89 90 /// Construct code sequence to extract the specific value from a `fir.box`. 91 mlir::Value ConvertFIRToLLVMPattern::getValueFromBox( 92 mlir::Location loc, TypePair boxTy, mlir::Value box, mlir::Type resultTy, 93 mlir::ConversionPatternRewriter &rewriter, int boxValue) const { 94 if (mlir::isa<mlir::LLVM::LLVMPointerType>(box.getType())) { 95 auto pty = getLlvmPtrType(resultTy.getContext()); 96 auto p = rewriter.create<mlir::LLVM::GEPOp>( 97 loc, pty, boxTy.llvm, box, 98 llvm::ArrayRef<mlir::LLVM::GEPArg>{0, boxValue}); 99 auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p); 100 attachTBAATag(loadOp, boxTy.fir, nullptr, p); 101 return loadOp; 102 } 103 return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, box, boxValue); 104 } 105 106 /// Method to construct code sequence to get the triple for dimension `dim` 107 /// from a box. 108 llvm::SmallVector<mlir::Value, 3> ConvertFIRToLLVMPattern::getDimsFromBox( 109 mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, TypePair boxTy, 110 mlir::Value box, mlir::Value dim, 111 mlir::ConversionPatternRewriter &rewriter) const { 112 mlir::Value l0 = 113 loadDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter); 114 mlir::Value l1 = 115 loadDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter); 116 mlir::Value l2 = 117 loadDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter); 118 return {l0, l1, l2}; 119 } 120 121 llvm::SmallVector<mlir::Value, 3> ConvertFIRToLLVMPattern::getDimsFromBox( 122 mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, TypePair boxTy, 123 mlir::Value box, int dim, mlir::ConversionPatternRewriter &rewriter) const { 124 mlir::Value l0 = 125 getDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter); 126 mlir::Value l1 = 127 getDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter); 128 mlir::Value l2 = 129 getDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter); 130 return {l0, l1, l2}; 131 } 132 133 mlir::Value ConvertFIRToLLVMPattern::loadDimFieldFromBox( 134 mlir::Location loc, TypePair boxTy, mlir::Value box, mlir::Value dim, 135 int off, mlir::Type ty, mlir::ConversionPatternRewriter &rewriter) const { 136 assert(mlir::isa<mlir::LLVM::LLVMPointerType>(box.getType()) && 137 "descriptor inquiry with runtime dim can only be done on descriptor " 138 "in memory"); 139 mlir::LLVM::GEPOp p = genGEP(loc, boxTy.llvm, rewriter, box, 0, 140 static_cast<int>(kDimsPosInBox), dim, off); 141 auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 142 attachTBAATag(loadOp, boxTy.fir, nullptr, p); 143 return loadOp; 144 } 145 146 mlir::Value ConvertFIRToLLVMPattern::getDimFieldFromBox( 147 mlir::Location loc, TypePair boxTy, mlir::Value box, int dim, int off, 148 mlir::Type ty, mlir::ConversionPatternRewriter &rewriter) const { 149 if (mlir::isa<mlir::LLVM::LLVMPointerType>(box.getType())) { 150 mlir::LLVM::GEPOp p = genGEP(loc, boxTy.llvm, rewriter, box, 0, 151 static_cast<int>(kDimsPosInBox), dim, off); 152 auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 153 attachTBAATag(loadOp, boxTy.fir, nullptr, p); 154 return loadOp; 155 } 156 return rewriter.create<mlir::LLVM::ExtractValueOp>( 157 loc, box, llvm::ArrayRef<std::int64_t>{kDimsPosInBox, dim, off}); 158 } 159 160 mlir::Value ConvertFIRToLLVMPattern::getStrideFromBox( 161 mlir::Location loc, TypePair boxTy, mlir::Value box, unsigned dim, 162 mlir::ConversionPatternRewriter &rewriter) const { 163 auto idxTy = lowerTy().indexType(); 164 return getDimFieldFromBox(loc, boxTy, box, dim, kDimStridePos, idxTy, 165 rewriter); 166 } 167 168 /// Read base address from a fir.box. Returned address has type ty. 169 mlir::Value ConvertFIRToLLVMPattern::getBaseAddrFromBox( 170 mlir::Location loc, TypePair boxTy, mlir::Value box, 171 mlir::ConversionPatternRewriter &rewriter) const { 172 mlir::Type resultTy = ::getLlvmPtrType(boxTy.llvm.getContext()); 173 return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kAddrPosInBox); 174 } 175 176 mlir::Value ConvertFIRToLLVMPattern::getElementSizeFromBox( 177 mlir::Location loc, mlir::Type resultTy, TypePair boxTy, mlir::Value box, 178 mlir::ConversionPatternRewriter &rewriter) const { 179 return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kElemLenPosInBox); 180 } 181 182 // Get the element type given an LLVM type that is of the form 183 // (array|struct|vector)+ and the provided indexes. 184 mlir::Type ConvertFIRToLLVMPattern::getBoxEleTy( 185 mlir::Type type, llvm::ArrayRef<std::int64_t> indexes) const { 186 for (unsigned i : indexes) { 187 if (auto t = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(type)) { 188 assert(!t.isOpaque() && i < t.getBody().size()); 189 type = t.getBody()[i]; 190 } else if (auto t = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(type)) { 191 type = t.getElementType(); 192 } else if (auto t = mlir::dyn_cast<mlir::VectorType>(type)) { 193 type = t.getElementType(); 194 } else { 195 fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()), 196 "request for invalid box element type"); 197 } 198 } 199 return type; 200 } 201 202 // Return LLVM type of the object described by a fir.box of \p boxType. 203 mlir::Type ConvertFIRToLLVMPattern::getLlvmObjectTypeFromBoxType( 204 mlir::Type boxType) const { 205 mlir::Type objectType = fir::dyn_cast_ptrOrBoxEleTy(boxType); 206 assert(objectType && "boxType must be a box type"); 207 return this->convertType(objectType); 208 } 209 210 /// Read the address of the type descriptor from a box. 211 mlir::Value ConvertFIRToLLVMPattern::loadTypeDescAddress( 212 mlir::Location loc, TypePair boxTy, mlir::Value box, 213 mlir::ConversionPatternRewriter &rewriter) const { 214 unsigned typeDescFieldId = getTypeDescFieldId(boxTy.fir); 215 mlir::Type tdescType = lowerTy().convertTypeDescType(rewriter.getContext()); 216 return getValueFromBox(loc, boxTy, box, tdescType, rewriter, typeDescFieldId); 217 } 218 219 // Load the attribute from the \p box and perform a check against \p maskValue 220 // The final comparison is implemented as `(attribute & maskValue) != 0`. 221 mlir::Value ConvertFIRToLLVMPattern::genBoxAttributeCheck( 222 mlir::Location loc, TypePair boxTy, mlir::Value box, 223 mlir::ConversionPatternRewriter &rewriter, unsigned maskValue) const { 224 mlir::Type attrTy = rewriter.getI32Type(); 225 mlir::Value attribute = 226 getValueFromBox(loc, boxTy, box, attrTy, rewriter, kAttributePosInBox); 227 mlir::LLVM::ConstantOp attrMask = genConstantOffset(loc, rewriter, maskValue); 228 auto maskRes = 229 rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask); 230 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 231 return rewriter.create<mlir::LLVM::ICmpOp>(loc, mlir::LLVM::ICmpPredicate::ne, 232 maskRes, c0); 233 } 234 235 // Find the Block in which the alloca should be inserted. 236 // The order to recursively find the proper block: 237 // 1. An OpenMP Op that will be outlined. 238 // 2. A LLVMFuncOp 239 // 3. The first ancestor that is an OpenMP Op or a LLVMFuncOp 240 mlir::Block * 241 ConvertFIRToLLVMPattern::getBlockForAllocaInsert(mlir::Operation *op) const { 242 if (auto iface = mlir::dyn_cast<mlir::omp::OutlineableOpenMPOpInterface>(op)) 243 return iface.getAllocaBlock(); 244 if (auto llvmFuncOp = mlir::dyn_cast<mlir::LLVM::LLVMFuncOp>(op)) 245 return &llvmFuncOp.front(); 246 return getBlockForAllocaInsert(op->getParentOp()); 247 } 248 249 // Generate an alloca of size 1 for an object of type \p llvmObjectTy in the 250 // allocation address space provided for the architecture in the DataLayout 251 // specification. If the address space is different from the devices 252 // program address space we perform a cast. In the case of most architectures 253 // the program and allocation address space will be the default of 0 and no 254 // cast will be emitted. 255 mlir::Value ConvertFIRToLLVMPattern::genAllocaAndAddrCastWithType( 256 mlir::Location loc, mlir::Type llvmObjectTy, unsigned alignment, 257 mlir::ConversionPatternRewriter &rewriter) const { 258 auto thisPt = rewriter.saveInsertionPoint(); 259 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); 260 if (mlir::isa<mlir::omp::DeclareReductionOp>(parentOp)) { 261 // DeclareReductionOp has multiple child regions. We want to get the first 262 // block of whichever of those regions we are currently in 263 mlir::Region *parentRegion = rewriter.getInsertionBlock()->getParent(); 264 rewriter.setInsertionPointToStart(&parentRegion->front()); 265 } else { 266 mlir::Block *insertBlock = getBlockForAllocaInsert(parentOp); 267 rewriter.setInsertionPointToStart(insertBlock); 268 } 269 auto size = genI32Constant(loc, rewriter, 1); 270 unsigned allocaAs = getAllocaAddressSpace(rewriter); 271 unsigned programAs = getProgramAddressSpace(rewriter); 272 273 mlir::Value al = rewriter.create<mlir::LLVM::AllocaOp>( 274 loc, ::getLlvmPtrType(llvmObjectTy.getContext(), allocaAs), llvmObjectTy, 275 size, alignment); 276 277 // if our allocation address space, is not the same as the program address 278 // space, then we must emit a cast to the program address space before use. 279 // An example case would be on AMDGPU, where the allocation address space is 280 // the numeric value 5 (private), and the program address space is 0 281 // (generic). 282 if (allocaAs != programAs) { 283 al = rewriter.create<mlir::LLVM::AddrSpaceCastOp>( 284 loc, ::getLlvmPtrType(llvmObjectTy.getContext(), programAs), al); 285 } 286 287 rewriter.restoreInsertionPoint(thisPt); 288 return al; 289 } 290 291 unsigned ConvertFIRToLLVMPattern::getAllocaAddressSpace( 292 mlir::ConversionPatternRewriter &rewriter) const { 293 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); 294 assert(parentOp != nullptr && 295 "expected insertion block to have parent operation"); 296 if (auto module = parentOp->getParentOfType<mlir::ModuleOp>()) 297 if (mlir::Attribute addrSpace = 298 mlir::DataLayout(module).getAllocaMemorySpace()) 299 return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt(); 300 return defaultAddressSpace; 301 } 302 303 unsigned ConvertFIRToLLVMPattern::getProgramAddressSpace( 304 mlir::ConversionPatternRewriter &rewriter) const { 305 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); 306 assert(parentOp != nullptr && 307 "expected insertion block to have parent operation"); 308 if (auto module = parentOp->getParentOfType<mlir::ModuleOp>()) 309 if (mlir::Attribute addrSpace = 310 mlir::DataLayout(module).getProgramMemorySpace()) 311 return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt(); 312 return defaultAddressSpace; 313 } 314 315 } // namespace fir 316