393 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/10//11//===----------------------------------------------------------------------===//12 13#include "flang/Optimizer/CodeGen/FIROpPatterns.h"14#include "flang/Optimizer/Builder/FIRBuilder.h"15#include "mlir/Dialect/OpenMP/OpenMPDialect.h"16#include "llvm/Support/Debug.h"17 18static inline mlir::Type getLlvmPtrType(mlir::MLIRContext *context,19 unsigned addressSpace = 0) {20 return mlir::LLVM::LLVMPointerType::get(context, addressSpace);21}22 23static unsigned getTypeDescFieldId(mlir::Type ty) {24 auto isArray = mlir::isa<fir::SequenceType>(fir::dyn_cast_ptrOrBoxEleTy(ty));25 return isArray ? kOptTypePtrPosInBox : kDimsPosInBox;26}27 28namespace fir {29 30ConvertFIRToLLVMPattern::ConvertFIRToLLVMPattern(31 llvm::StringRef rootOpName, mlir::MLIRContext *context,32 const fir::LLVMTypeConverter &typeConverter,33 const fir::FIRToLLVMPassOptions &options, mlir::PatternBenefit benefit)34 : ConvertToLLVMPattern(rootOpName, context, typeConverter, benefit),35 options(options) {}36 37// Convert FIR type to LLVM without turning fir.box<T> into memory38// reference.39mlir::Type40ConvertFIRToLLVMPattern::convertObjectType(mlir::Type firType) const {41 if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(firType))42 return lowerTy().convertBoxTypeAsStruct(boxTy);43 return lowerTy().convertType(firType);44}45 46mlir::LLVM::ConstantOp ConvertFIRToLLVMPattern::genI32Constant(47 mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,48 int value) const {49 mlir::Type i32Ty = rewriter.getI32Type();50 mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value);51 return mlir::LLVM::ConstantOp::create(rewriter, loc, i32Ty, attr);52}53 54mlir::LLVM::ConstantOp ConvertFIRToLLVMPattern::genConstantOffset(55 mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,56 int offset) const {57 mlir::Type ity = lowerTy().offsetType();58 mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset);59 return mlir::LLVM::ConstantOp::create(rewriter, loc, ity, cattr);60}61 62/// Perform an extension or truncation as needed on an integer value. Lowering63/// to the specific target may involve some sign-extending or truncation of64/// values, particularly to fit them from abstract box types to the65/// appropriate reified structures.66mlir::Value ConvertFIRToLLVMPattern::integerCast(67 mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,68 mlir::Type ty, mlir::Value val, bool fold) const {69 auto valTy = val.getType();70 // If the value was not yet lowered, lower its type so that it can71 // 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 (fold) {77 if (toSize < fromSize)78 return rewriter.createOrFold<mlir::LLVM::TruncOp>(loc, ty, val);79 if (toSize > fromSize)80 return rewriter.createOrFold<mlir::LLVM::SExtOp>(loc, ty, val);81 } else {82 if (toSize < fromSize)83 return mlir::LLVM::TruncOp::create(rewriter, loc, ty, val);84 if (toSize > fromSize)85 return mlir::LLVM::SExtOp::create(rewriter, loc, ty, val);86 }87 return val;88}89 90fir::ConvertFIRToLLVMPattern::TypePair91ConvertFIRToLLVMPattern::getBoxTypePair(mlir::Type firBoxTy) const {92 mlir::Type llvmBoxTy =93 lowerTy().convertBoxTypeAsStruct(mlir::cast<fir::BaseBoxType>(firBoxTy));94 return TypePair{firBoxTy, llvmBoxTy};95}96 97/// Construct code sequence to extract the specific value from a `fir.box`.98mlir::Value ConvertFIRToLLVMPattern::getValueFromBox(99 mlir::Location loc, TypePair boxTy, mlir::Value box, mlir::Type resultTy,100 mlir::ConversionPatternRewriter &rewriter, int boxValue) const {101 if (mlir::isa<mlir::LLVM::LLVMPointerType>(box.getType())) {102 auto pty = getLlvmPtrType(resultTy.getContext());103 auto p = mlir::LLVM::GEPOp::create(104 rewriter, loc, pty, boxTy.llvm, box,105 llvm::ArrayRef<mlir::LLVM::GEPArg>{0, boxValue});106 auto fldTy = getBoxEleTy(boxTy.llvm, {boxValue});107 auto loadOp = mlir::LLVM::LoadOp::create(rewriter, loc, fldTy, p);108 auto castOp = integerCast(loc, rewriter, resultTy, loadOp);109 attachTBAATag(loadOp, boxTy.fir, nullptr, p);110 return castOp;111 }112 return mlir::LLVM::ExtractValueOp::create(rewriter, loc, box, boxValue);113}114 115/// Method to construct code sequence to get the triple for dimension `dim`116/// from a box.117llvm::SmallVector<mlir::Value, 3> ConvertFIRToLLVMPattern::getDimsFromBox(118 mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, TypePair boxTy,119 mlir::Value box, mlir::Value dim,120 mlir::ConversionPatternRewriter &rewriter) const {121 mlir::Value l0 =122 loadDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter);123 mlir::Value l1 =124 loadDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter);125 mlir::Value l2 =126 loadDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter);127 return {l0, l1, l2};128}129 130llvm::SmallVector<mlir::Value, 3> ConvertFIRToLLVMPattern::getDimsFromBox(131 mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, TypePair boxTy,132 mlir::Value box, int dim, mlir::ConversionPatternRewriter &rewriter) const {133 mlir::Value l0 =134 getDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter);135 mlir::Value l1 =136 getDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter);137 mlir::Value l2 =138 getDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter);139 return {l0, l1, l2};140}141 142mlir::Value ConvertFIRToLLVMPattern::loadDimFieldFromBox(143 mlir::Location loc, TypePair boxTy, mlir::Value box, mlir::Value dim,144 int off, mlir::Type ty, mlir::ConversionPatternRewriter &rewriter) const {145 assert(mlir::isa<mlir::LLVM::LLVMPointerType>(box.getType()) &&146 "descriptor inquiry with runtime dim can only be done on descriptor "147 "in memory");148 mlir::LLVM::GEPOp p = genGEP(loc, boxTy.llvm, rewriter, box, 0,149 static_cast<int>(kDimsPosInBox), dim, off);150 auto loadOp = mlir::LLVM::LoadOp::create(rewriter, loc, ty, p);151 attachTBAATag(loadOp, boxTy.fir, nullptr, p);152 return loadOp;153}154 155mlir::Value ConvertFIRToLLVMPattern::getDimFieldFromBox(156 mlir::Location loc, TypePair boxTy, mlir::Value box, int dim, int off,157 mlir::Type ty, mlir::ConversionPatternRewriter &rewriter) const {158 if (mlir::isa<mlir::LLVM::LLVMPointerType>(box.getType())) {159 mlir::LLVM::GEPOp p = genGEP(loc, boxTy.llvm, rewriter, box, 0,160 static_cast<int>(kDimsPosInBox), dim, off);161 auto loadOp = mlir::LLVM::LoadOp::create(rewriter, loc, ty, p);162 attachTBAATag(loadOp, boxTy.fir, nullptr, p);163 return loadOp;164 }165 return mlir::LLVM::ExtractValueOp::create(166 rewriter, loc, box,167 llvm::ArrayRef<std::int64_t>{kDimsPosInBox, dim, off});168}169 170mlir::Value ConvertFIRToLLVMPattern::getStrideFromBox(171 mlir::Location loc, TypePair boxTy, mlir::Value box, unsigned dim,172 mlir::ConversionPatternRewriter &rewriter) const {173 auto idxTy = lowerTy().indexType();174 return getDimFieldFromBox(loc, boxTy, box, dim, kDimStridePos, idxTy,175 rewriter);176}177 178/// Read base address from a fir.box. Returned address has type ty.179mlir::Value ConvertFIRToLLVMPattern::getBaseAddrFromBox(180 mlir::Location loc, TypePair boxTy, mlir::Value box,181 mlir::ConversionPatternRewriter &rewriter) const {182 mlir::Type resultTy = ::getLlvmPtrType(boxTy.llvm.getContext());183 return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kAddrPosInBox);184}185 186mlir::Value ConvertFIRToLLVMPattern::getElementSizeFromBox(187 mlir::Location loc, mlir::Type resultTy, TypePair boxTy, mlir::Value box,188 mlir::ConversionPatternRewriter &rewriter) const {189 return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kElemLenPosInBox);190}191 192/// Read base address from a fir.box. Returned address has type ty.193mlir::Value ConvertFIRToLLVMPattern::getRankFromBox(194 mlir::Location loc, TypePair boxTy, mlir::Value box,195 mlir::ConversionPatternRewriter &rewriter) const {196 mlir::Type resultTy = getBoxEleTy(boxTy.llvm, {kRankPosInBox});197 return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kRankPosInBox);198}199 200/// Read the extra field from a fir.box.201mlir::Value ConvertFIRToLLVMPattern::getExtraFromBox(202 mlir::Location loc, TypePair boxTy, mlir::Value box,203 mlir::ConversionPatternRewriter &rewriter) const {204 mlir::Type resultTy = getBoxEleTy(boxTy.llvm, {kExtraPosInBox});205 return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kExtraPosInBox);206}207 208// Get the element type given an LLVM type that is of the form209// (array|struct|vector)+ and the provided indexes.210mlir::Type ConvertFIRToLLVMPattern::getBoxEleTy(211 mlir::Type type, llvm::ArrayRef<std::int64_t> indexes) const {212 for (unsigned i : indexes) {213 if (auto t = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(type)) {214 assert(!t.isOpaque() && i < t.getBody().size());215 type = t.getBody()[i];216 } else if (auto t = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(type)) {217 type = t.getElementType();218 } else if (auto t = mlir::dyn_cast<mlir::VectorType>(type)) {219 type = t.getElementType();220 } else {221 fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()),222 "request for invalid box element type");223 }224 }225 return type;226}227 228// Return LLVM type of the object described by a fir.box of \p boxType.229mlir::Type ConvertFIRToLLVMPattern::getLlvmObjectTypeFromBoxType(230 mlir::Type boxType) const {231 mlir::Type objectType = fir::dyn_cast_ptrOrBoxEleTy(boxType);232 assert(objectType && "boxType must be a box type");233 return this->convertType(objectType);234}235 236/// Read the address of the type descriptor from a box.237mlir::Value ConvertFIRToLLVMPattern::loadTypeDescAddress(238 mlir::Location loc, TypePair boxTy, mlir::Value box,239 mlir::ConversionPatternRewriter &rewriter) const {240 unsigned typeDescFieldId = getTypeDescFieldId(boxTy.fir);241 mlir::Type tdescType = lowerTy().convertTypeDescType(rewriter.getContext());242 return getValueFromBox(loc, boxTy, box, tdescType, rewriter, typeDescFieldId);243}244 245// Load the attribute from the \p box and perform a check against \p maskValue246// The final comparison is implemented as `(attribute & maskValue) != 0`.247mlir::Value ConvertFIRToLLVMPattern::genBoxAttributeCheck(248 mlir::Location loc, TypePair boxTy, mlir::Value box,249 mlir::ConversionPatternRewriter &rewriter, unsigned maskValue) const {250 mlir::Type attrTy = rewriter.getI32Type();251 mlir::Value attribute =252 getValueFromBox(loc, boxTy, box, attrTy, rewriter, kAttributePosInBox);253 mlir::LLVM::ConstantOp attrMask = genConstantOffset(loc, rewriter, maskValue);254 auto maskRes =255 mlir::LLVM::AndOp::create(rewriter, loc, attrTy, attribute, attrMask);256 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);257 return mlir::LLVM::ICmpOp::create(rewriter, loc,258 mlir::LLVM::ICmpPredicate::ne, maskRes, c0);259}260 261mlir::Value ConvertFIRToLLVMPattern::computeBoxSize(262 mlir::Location loc, TypePair boxTy, mlir::Value box,263 mlir::ConversionPatternRewriter &rewriter) const {264 auto firBoxType = mlir::dyn_cast<fir::BaseBoxType>(boxTy.fir);265 assert(firBoxType && "must be a BaseBoxType");266 const mlir::DataLayout &dl = lowerTy().getDataLayout();267 if (!firBoxType.isAssumedRank())268 return genConstantOffset(loc, rewriter, dl.getTypeSize(boxTy.llvm));269 fir::BaseBoxType firScalarBoxType = firBoxType.getBoxTypeWithNewShape(0);270 mlir::Type llvmScalarBoxType =271 lowerTy().convertBoxTypeAsStruct(firScalarBoxType);272 llvm::TypeSize scalarBoxSizeCst = dl.getTypeSize(llvmScalarBoxType);273 mlir::Value scalarBoxSize =274 genConstantOffset(loc, rewriter, scalarBoxSizeCst);275 mlir::Value rawRank = getRankFromBox(loc, boxTy, box, rewriter);276 mlir::Value rank =277 integerCast(loc, rewriter, scalarBoxSize.getType(), rawRank);278 mlir::Type llvmDimsType = getBoxEleTy(boxTy.llvm, {kDimsPosInBox, 1});279 llvm::TypeSize sizePerDimCst = dl.getTypeSize(llvmDimsType);280 assert((scalarBoxSizeCst + sizePerDimCst ==281 dl.getTypeSize(lowerTy().convertBoxTypeAsStruct(282 firBoxType.getBoxTypeWithNewShape(1)))) &&283 "descriptor layout requires adding padding for dim field");284 mlir::Value sizePerDim = genConstantOffset(loc, rewriter, sizePerDimCst);285 mlir::Value dimsSize = mlir::LLVM::MulOp::create(286 rewriter, loc, sizePerDim.getType(), sizePerDim, rank);287 mlir::Value size = mlir::LLVM::AddOp::create(288 rewriter, loc, scalarBoxSize.getType(), scalarBoxSize, dimsSize);289 return size;290}291 292// Find the Block in which the alloca should be inserted.293// The order to recursively find the proper block:294// 1. An OpenMP Op that will be outlined.295// 2. An OpenMP or OpenACC Op with one or more regions holding executable code.296// 3. A LLVMFuncOp297// 4. The first ancestor that is one of the above.298mlir::Block *ConvertFIRToLLVMPattern::getBlockForAllocaInsert(299 mlir::Operation *op, mlir::Region *parentRegion) const {300 if (auto iface = mlir::dyn_cast<mlir::omp::OutlineableOpenMPOpInterface>(op))301 return iface.getAllocaBlock();302 if (auto recipeIface = mlir::dyn_cast<mlir::accomp::RecipeInterface>(op))303 return recipeIface.getAllocaBlock(*parentRegion);304 if (auto llvmFuncOp = mlir::dyn_cast<mlir::LLVM::LLVMFuncOp>(op))305 return &llvmFuncOp.front();306 307 return getBlockForAllocaInsert(op->getParentOp(), parentRegion);308}309 310// Generate an alloca of size 1 for an object of type \p llvmObjectTy in the311// allocation address space provided for the architecture in the DataLayout312// specification. If the address space is different from the devices313// program address space we perform a cast. In the case of most architectures314// the program and allocation address space will be the default of 0 and no315// cast will be emitted.316mlir::Value ConvertFIRToLLVMPattern::genAllocaAndAddrCastWithType(317 mlir::Location loc, mlir::Type llvmObjectTy, unsigned alignment,318 mlir::ConversionPatternRewriter &rewriter) const {319 auto thisPt = rewriter.saveInsertionPoint();320 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();321 mlir::Region *parentRegion = rewriter.getInsertionBlock()->getParent();322 mlir::Block *insertBlock = getBlockForAllocaInsert(parentOp, parentRegion);323 rewriter.setInsertionPointToStart(insertBlock);324 auto size = genI32Constant(loc, rewriter, 1);325 unsigned allocaAs = getAllocaAddressSpace(rewriter);326 unsigned programAs = getProgramAddressSpace(rewriter);327 328 mlir::Value al = mlir::LLVM::AllocaOp::create(329 rewriter, loc, ::getLlvmPtrType(llvmObjectTy.getContext(), allocaAs),330 llvmObjectTy, size, alignment);331 332 // if our allocation address space, is not the same as the program address333 // space, then we must emit a cast to the program address space before use.334 // An example case would be on AMDGPU, where the allocation address space is335 // the numeric value 5 (private), and the program address space is 0336 // (generic).337 if (allocaAs != programAs) {338 al = mlir::LLVM::AddrSpaceCastOp::create(339 rewriter, loc, ::getLlvmPtrType(llvmObjectTy.getContext(), programAs),340 al);341 }342 343 rewriter.restoreInsertionPoint(thisPt);344 return al;345}346 347unsigned ConvertFIRToLLVMPattern::getAllocaAddressSpace(348 mlir::ConversionPatternRewriter &rewriter) const {349 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();350 assert(parentOp != nullptr &&351 "expected insertion block to have parent operation");352 auto module = mlir::isa<mlir::ModuleOp>(parentOp)353 ? mlir::cast<mlir::ModuleOp>(parentOp)354 : parentOp->getParentOfType<mlir::ModuleOp>();355 if (module)356 if (mlir::Attribute addrSpace =357 mlir::DataLayout(module).getAllocaMemorySpace())358 return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt();359 return defaultAddressSpace;360}361 362unsigned ConvertFIRToLLVMPattern::getProgramAddressSpace(363 mlir::ConversionPatternRewriter &rewriter) const {364 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();365 assert(parentOp != nullptr &&366 "expected insertion block to have parent operation");367 auto module = mlir::isa<mlir::ModuleOp>(parentOp)368 ? mlir::cast<mlir::ModuleOp>(parentOp)369 : parentOp->getParentOfType<mlir::ModuleOp>();370 if (module)371 if (mlir::Attribute addrSpace =372 mlir::DataLayout(module).getProgramMemorySpace())373 return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt();374 return defaultAddressSpace;375}376 377unsigned ConvertFIRToLLVMPattern::getGlobalAddressSpace(378 mlir::ConversionPatternRewriter &rewriter) const {379 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();380 assert(parentOp != nullptr &&381 "expected insertion block to have parent operation");382 auto module = mlir::isa<mlir::ModuleOp>(parentOp)383 ? mlir::cast<mlir::ModuleOp>(parentOp)384 : parentOp->getParentOfType<mlir::ModuleOp>();385 if (module)386 if (mlir::Attribute addrSpace =387 mlir::DataLayout(module).getGlobalMemorySpace())388 return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt();389 return defaultAddressSpace;390}391 392} // namespace fir393