| //===-- FIRBuilder.cpp ----------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "flang/Optimizer/Builder/FIRBuilder.h" |
| #include "flang/Optimizer/Builder/BoxValue.h" |
| #include "flang/Optimizer/Builder/Character.h" |
| #include "flang/Optimizer/Builder/Complex.h" |
| #include "flang/Optimizer/Builder/MutableBox.h" |
| #include "flang/Optimizer/Builder/Runtime/Assign.h" |
| #include "flang/Optimizer/Builder/Runtime/Derived.h" |
| #include "flang/Optimizer/Builder/Todo.h" |
| #include "flang/Optimizer/Dialect/FIRAttr.h" |
| #include "flang/Optimizer/Dialect/FIROpsSupport.h" |
| #include "flang/Optimizer/Dialect/FIRType.h" |
| #include "flang/Optimizer/Support/FatalError.h" |
| #include "flang/Optimizer/Support/InternalNames.h" |
| #include "mlir/Dialect/LLVMIR/LLVMDialect.h" |
| #include "mlir/Dialect/OpenACC/OpenACC.h" |
| #include "mlir/Dialect/OpenMP/OpenMPDialect.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/MD5.h" |
| #include <optional> |
| |
| static llvm::cl::opt<std::size_t> |
| nameLengthHashSize("length-to-hash-string-literal", |
| llvm::cl::desc("string literals that exceed this length" |
| " will use a hash value as their symbol " |
| "name"), |
| llvm::cl::init(32)); |
| |
| mlir::func::FuncOp |
| fir::FirOpBuilder::createFunction(mlir::Location loc, mlir::ModuleOp module, |
| llvm::StringRef name, mlir::FunctionType ty, |
| mlir::SymbolTable *symbolTable) { |
| return fir::createFuncOp(loc, module, name, ty, /*attrs*/ {}, symbolTable); |
| } |
| |
| mlir::func::FuncOp |
| fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp, |
| const mlir::SymbolTable *symbolTable, |
| llvm::StringRef name) { |
| if (symbolTable) |
| if (auto func = symbolTable->lookup<mlir::func::FuncOp>(name)) { |
| #ifdef EXPENSIVE_CHECKS |
| assert(func == modOp.lookupSymbol<mlir::func::FuncOp>(name) && |
| "symbolTable and module out of sync"); |
| #endif |
| return func; |
| } |
| return modOp.lookupSymbol<mlir::func::FuncOp>(name); |
| } |
| |
| mlir::func::FuncOp |
| fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp, |
| const mlir::SymbolTable *symbolTable, |
| mlir::SymbolRefAttr symbol) { |
| if (symbolTable) |
| if (auto func = symbolTable->lookup<mlir::func::FuncOp>( |
| symbol.getLeafReference())) { |
| #ifdef EXPENSIVE_CHECKS |
| assert(func == modOp.lookupSymbol<mlir::func::FuncOp>(symbol) && |
| "symbolTable and module out of sync"); |
| #endif |
| return func; |
| } |
| return modOp.lookupSymbol<mlir::func::FuncOp>(symbol); |
| } |
| |
| fir::GlobalOp |
| fir::FirOpBuilder::getNamedGlobal(mlir::ModuleOp modOp, |
| const mlir::SymbolTable *symbolTable, |
| llvm::StringRef name) { |
| if (symbolTable) |
| if (auto global = symbolTable->lookup<fir::GlobalOp>(name)) { |
| #ifdef EXPENSIVE_CHECKS |
| assert(global == modOp.lookupSymbol<fir::GlobalOp>(name) && |
| "symbolTable and module out of sync"); |
| #endif |
| return global; |
| } |
| return modOp.lookupSymbol<fir::GlobalOp>(name); |
| } |
| |
| mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy) { |
| assert(!mlir::isa<fir::ReferenceType>(eleTy) && "cannot be a reference type"); |
| return fir::ReferenceType::get(eleTy); |
| } |
| |
| mlir::Type fir::FirOpBuilder::getVarLenSeqTy(mlir::Type eleTy, unsigned rank) { |
| fir::SequenceType::Shape shape(rank, fir::SequenceType::getUnknownExtent()); |
| return fir::SequenceType::get(shape, eleTy); |
| } |
| |
| mlir::Type fir::FirOpBuilder::getRealType(int kind) { |
| switch (kindMap.getRealTypeID(kind)) { |
| case llvm::Type::TypeID::HalfTyID: |
| return mlir::FloatType::getF16(getContext()); |
| case llvm::Type::TypeID::BFloatTyID: |
| return mlir::FloatType::getBF16(getContext()); |
| case llvm::Type::TypeID::FloatTyID: |
| return mlir::FloatType::getF32(getContext()); |
| case llvm::Type::TypeID::DoubleTyID: |
| return mlir::FloatType::getF64(getContext()); |
| case llvm::Type::TypeID::X86_FP80TyID: |
| return mlir::FloatType::getF80(getContext()); |
| case llvm::Type::TypeID::FP128TyID: |
| return mlir::FloatType::getF128(getContext()); |
| default: |
| fir::emitFatalError(mlir::UnknownLoc::get(getContext()), |
| "unsupported type !fir.real<kind>"); |
| } |
| } |
| |
| mlir::Value fir::FirOpBuilder::createNullConstant(mlir::Location loc, |
| mlir::Type ptrType) { |
| auto ty = ptrType ? ptrType : getRefType(getNoneType()); |
| return create<fir::ZeroOp>(loc, ty); |
| } |
| |
| mlir::Value fir::FirOpBuilder::createIntegerConstant(mlir::Location loc, |
| mlir::Type ty, |
| std::int64_t cst) { |
| assert((cst >= 0 || mlir::isa<mlir::IndexType>(ty) || |
| mlir::cast<mlir::IntegerType>(ty).getWidth() <= 64) && |
| "must use APint"); |
| return create<mlir::arith::ConstantOp>(loc, ty, getIntegerAttr(ty, cst)); |
| } |
| |
| mlir::Value fir::FirOpBuilder::createAllOnesInteger(mlir::Location loc, |
| mlir::Type ty) { |
| if (mlir::isa<mlir::IndexType>(ty)) |
| return createIntegerConstant(loc, ty, -1); |
| llvm::APInt allOnes = |
| llvm::APInt::getAllOnes(mlir::cast<mlir::IntegerType>(ty).getWidth()); |
| return create<mlir::arith::ConstantOp>(loc, ty, getIntegerAttr(ty, allOnes)); |
| } |
| |
| mlir::Value |
| fir::FirOpBuilder::createRealConstant(mlir::Location loc, mlir::Type fltTy, |
| llvm::APFloat::integerPart val) { |
| auto apf = [&]() -> llvm::APFloat { |
| if (auto ty = mlir::dyn_cast<fir::RealType>(fltTy)) |
| return llvm::APFloat(kindMap.getFloatSemantics(ty.getFKind()), val); |
| if (fltTy.isF16()) |
| return llvm::APFloat(llvm::APFloat::IEEEhalf(), val); |
| if (fltTy.isBF16()) |
| return llvm::APFloat(llvm::APFloat::BFloat(), val); |
| if (fltTy.isF32()) |
| return llvm::APFloat(llvm::APFloat::IEEEsingle(), val); |
| if (fltTy.isF64()) |
| return llvm::APFloat(llvm::APFloat::IEEEdouble(), val); |
| if (fltTy.isF80()) |
| return llvm::APFloat(llvm::APFloat::x87DoubleExtended(), val); |
| if (fltTy.isF128()) |
| return llvm::APFloat(llvm::APFloat::IEEEquad(), val); |
| llvm_unreachable("unhandled MLIR floating-point type"); |
| }; |
| return createRealConstant(loc, fltTy, apf()); |
| } |
| |
| mlir::Value fir::FirOpBuilder::createRealConstant(mlir::Location loc, |
| mlir::Type fltTy, |
| const llvm::APFloat &value) { |
| if (mlir::isa<mlir::FloatType>(fltTy)) { |
| auto attr = getFloatAttr(fltTy, value); |
| return create<mlir::arith::ConstantOp>(loc, fltTy, attr); |
| } |
| llvm_unreachable("should use builtin floating-point type"); |
| } |
| |
| llvm::SmallVector<mlir::Value> |
| fir::factory::elideExtentsAlreadyInType(mlir::Type type, |
| mlir::ValueRange shape) { |
| auto arrTy = mlir::dyn_cast<fir::SequenceType>(type); |
| if (shape.empty() || !arrTy) |
| return {}; |
| // elide the constant dimensions before construction |
| assert(shape.size() == arrTy.getDimension()); |
| llvm::SmallVector<mlir::Value> dynamicShape; |
| auto typeShape = arrTy.getShape(); |
| for (unsigned i = 0, end = arrTy.getDimension(); i < end; ++i) |
| if (typeShape[i] == fir::SequenceType::getUnknownExtent()) |
| dynamicShape.push_back(shape[i]); |
| return dynamicShape; |
| } |
| |
| llvm::SmallVector<mlir::Value> |
| fir::factory::elideLengthsAlreadyInType(mlir::Type type, |
| mlir::ValueRange lenParams) { |
| if (lenParams.empty()) |
| return {}; |
| if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(type)) |
| type = arrTy.getEleTy(); |
| if (fir::hasDynamicSize(type)) |
| return lenParams; |
| return {}; |
| } |
| |
| /// Allocate a local variable. |
| /// A local variable ought to have a name in the source code. |
| mlir::Value fir::FirOpBuilder::allocateLocal( |
| mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName, |
| llvm::StringRef name, bool pinned, llvm::ArrayRef<mlir::Value> shape, |
| llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) { |
| // Convert the shape extents to `index`, as needed. |
| llvm::SmallVector<mlir::Value> indices; |
| llvm::SmallVector<mlir::Value> elidedShape = |
| fir::factory::elideExtentsAlreadyInType(ty, shape); |
| llvm::SmallVector<mlir::Value> elidedLenParams = |
| fir::factory::elideLengthsAlreadyInType(ty, lenParams); |
| auto idxTy = getIndexType(); |
| for (mlir::Value sh : elidedShape) |
| indices.push_back(createConvert(loc, idxTy, sh)); |
| // Add a target attribute, if needed. |
| llvm::SmallVector<mlir::NamedAttribute> attrs; |
| if (asTarget) |
| attrs.emplace_back( |
| mlir::StringAttr::get(getContext(), fir::getTargetAttrName()), |
| getUnitAttr()); |
| // Create the local variable. |
| if (name.empty()) { |
| if (uniqName.empty()) |
| return create<fir::AllocaOp>(loc, ty, pinned, elidedLenParams, indices, |
| attrs); |
| return create<fir::AllocaOp>(loc, ty, uniqName, pinned, elidedLenParams, |
| indices, attrs); |
| } |
| return create<fir::AllocaOp>(loc, ty, uniqName, name, pinned, elidedLenParams, |
| indices, attrs); |
| } |
| |
| mlir::Value fir::FirOpBuilder::allocateLocal( |
| mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName, |
| llvm::StringRef name, llvm::ArrayRef<mlir::Value> shape, |
| llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) { |
| return allocateLocal(loc, ty, uniqName, name, /*pinned=*/false, shape, |
| lenParams, asTarget); |
| } |
| |
| /// Get the block for adding Allocas. |
| mlir::Block *fir::FirOpBuilder::getAllocaBlock() { |
| if (auto ompOutlineableIface = |
| getRegion() |
| .getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>()) { |
| return ompOutlineableIface.getAllocaBlock(); |
| } |
| if (getRegion().getParentOfType<mlir::omp::DeclareReductionOp>()) |
| return &getRegion().front(); |
| if (auto accRecipeIface = |
| getRegion().getParentOfType<mlir::acc::RecipeInterface>()) { |
| return accRecipeIface.getAllocaBlock(getRegion()); |
| } |
| |
| return getEntryBlock(); |
| } |
| |
| mlir::Value fir::FirOpBuilder::createTemporaryAlloc( |
| mlir::Location loc, mlir::Type type, llvm::StringRef name, |
| mlir::ValueRange lenParams, mlir::ValueRange shape, |
| llvm::ArrayRef<mlir::NamedAttribute> attrs) { |
| assert(!mlir::isa<fir::ReferenceType>(type) && "cannot be a reference"); |
| // If the alloca is inside an OpenMP Op which will be outlined then pin |
| // the alloca here. |
| const bool pinned = |
| getRegion().getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>(); |
| mlir::Value temp = |
| create<fir::AllocaOp>(loc, type, /*unique_name=*/llvm::StringRef{}, name, |
| pinned, lenParams, shape, attrs); |
| return temp; |
| } |
| |
| /// Create a temporary variable on the stack. Anonymous temporaries have no |
| /// `name` value. Temporaries do not require a uniqued name. |
| mlir::Value |
| fir::FirOpBuilder::createTemporary(mlir::Location loc, mlir::Type type, |
| llvm::StringRef name, mlir::ValueRange shape, |
| mlir::ValueRange lenParams, |
| llvm::ArrayRef<mlir::NamedAttribute> attrs) { |
| llvm::SmallVector<mlir::Value> dynamicShape = |
| fir::factory::elideExtentsAlreadyInType(type, shape); |
| llvm::SmallVector<mlir::Value> dynamicLength = |
| fir::factory::elideLengthsAlreadyInType(type, lenParams); |
| InsertPoint insPt; |
| const bool hoistAlloc = dynamicShape.empty() && dynamicLength.empty(); |
| if (hoistAlloc) { |
| insPt = saveInsertionPoint(); |
| setInsertionPointToStart(getAllocaBlock()); |
| } |
| |
| mlir::Value ae = |
| createTemporaryAlloc(loc, type, name, dynamicLength, dynamicShape, attrs); |
| |
| if (hoistAlloc) |
| restoreInsertionPoint(insPt); |
| return ae; |
| } |
| |
| mlir::Value fir::FirOpBuilder::createHeapTemporary( |
| mlir::Location loc, mlir::Type type, llvm::StringRef name, |
| mlir::ValueRange shape, mlir::ValueRange lenParams, |
| llvm::ArrayRef<mlir::NamedAttribute> attrs) { |
| llvm::SmallVector<mlir::Value> dynamicShape = |
| fir::factory::elideExtentsAlreadyInType(type, shape); |
| llvm::SmallVector<mlir::Value> dynamicLength = |
| fir::factory::elideLengthsAlreadyInType(type, lenParams); |
| |
| assert(!mlir::isa<fir::ReferenceType>(type) && "cannot be a reference"); |
| return create<fir::AllocMemOp>(loc, type, /*unique_name=*/llvm::StringRef{}, |
| name, dynamicLength, dynamicShape, attrs); |
| } |
| |
| /// Create a global variable in the (read-only) data section. A global variable |
| /// must have a unique name to identify and reference it. |
| fir::GlobalOp fir::FirOpBuilder::createGlobal( |
| mlir::Location loc, mlir::Type type, llvm::StringRef name, |
| mlir::StringAttr linkage, mlir::Attribute value, bool isConst, |
| bool isTarget, fir::CUDADataAttributeAttr cudaAttr) { |
| if (auto global = getNamedGlobal(name)) |
| return global; |
| auto module = getModule(); |
| auto insertPt = saveInsertionPoint(); |
| setInsertionPoint(module.getBody(), module.getBody()->end()); |
| llvm::SmallVector<mlir::NamedAttribute> attrs; |
| if (cudaAttr) { |
| auto globalOpName = mlir::OperationName(fir::GlobalOp::getOperationName(), |
| module.getContext()); |
| attrs.push_back(mlir::NamedAttribute( |
| fir::GlobalOp::getCudaAttrAttrName(globalOpName), cudaAttr)); |
| } |
| auto glob = create<fir::GlobalOp>(loc, name, isConst, isTarget, type, value, |
| linkage, attrs); |
| restoreInsertionPoint(insertPt); |
| if (symbolTable) |
| symbolTable->insert(glob); |
| return glob; |
| } |
| |
| fir::GlobalOp fir::FirOpBuilder::createGlobal( |
| mlir::Location loc, mlir::Type type, llvm::StringRef name, bool isConst, |
| bool isTarget, std::function<void(FirOpBuilder &)> bodyBuilder, |
| mlir::StringAttr linkage, fir::CUDADataAttributeAttr cudaAttr) { |
| if (auto global = getNamedGlobal(name)) |
| return global; |
| auto module = getModule(); |
| auto insertPt = saveInsertionPoint(); |
| setInsertionPoint(module.getBody(), module.getBody()->end()); |
| auto glob = create<fir::GlobalOp>(loc, name, isConst, isTarget, type, |
| mlir::Attribute{}, linkage); |
| auto ®ion = glob.getRegion(); |
| region.push_back(new mlir::Block); |
| auto &block = glob.getRegion().back(); |
| setInsertionPointToStart(&block); |
| bodyBuilder(*this); |
| restoreInsertionPoint(insertPt); |
| if (symbolTable) |
| symbolTable->insert(glob); |
| return glob; |
| } |
| |
| mlir::Value fir::FirOpBuilder::convertWithSemantics( |
| mlir::Location loc, mlir::Type toTy, mlir::Value val, |
| bool allowCharacterConversion, bool allowRebox) { |
| assert(toTy && "store location must be typed"); |
| auto fromTy = val.getType(); |
| if (fromTy == toTy) |
| return val; |
| fir::factory::Complex helper{*this, loc}; |
| if ((fir::isa_real(fromTy) || fir::isa_integer(fromTy)) && |
| fir::isa_complex(toTy)) { |
| // imaginary part is zero |
| auto eleTy = helper.getComplexPartType(toTy); |
| auto cast = createConvert(loc, eleTy, val); |
| llvm::APFloat zero{kindMap.getFloatSemantics( |
| mlir::cast<fir::ComplexType>(toTy).getFKind()), |
| 0}; |
| auto imag = createRealConstant(loc, eleTy, zero); |
| return helper.createComplex(toTy, cast, imag); |
| } |
| if (fir::isa_complex(fromTy) && |
| (fir::isa_integer(toTy) || fir::isa_real(toTy))) { |
| // drop the imaginary part |
| auto rp = helper.extractComplexPart(val, /*isImagPart=*/false); |
| return createConvert(loc, toTy, rp); |
| } |
| if (allowCharacterConversion) { |
| if (mlir::isa<fir::BoxCharType>(fromTy)) { |
| // Extract the address of the character string and pass it |
| fir::factory::CharacterExprHelper charHelper{*this, loc}; |
| std::pair<mlir::Value, mlir::Value> unboxchar = |
| charHelper.createUnboxChar(val); |
| return createConvert(loc, toTy, unboxchar.first); |
| } |
| if (auto boxType = mlir::dyn_cast<fir::BoxCharType>(toTy)) { |
| // Extract the address of the actual argument and create a boxed |
| // character value with an undefined length |
| // TODO: We should really calculate the total size of the actual |
| // argument in characters and use it as the length of the string |
| auto refType = getRefType(boxType.getEleTy()); |
| mlir::Value charBase = createConvert(loc, refType, val); |
| mlir::Value unknownLen = create<fir::UndefOp>(loc, getIndexType()); |
| fir::factory::CharacterExprHelper charHelper{*this, loc}; |
| return charHelper.createEmboxChar(charBase, unknownLen); |
| } |
| } |
| if (fir::isa_ref_type(toTy) && fir::isa_box_type(fromTy)) { |
| // Call is expecting a raw data pointer, not a box. Get the data pointer out |
| // of the box and pass that. |
| assert((fir::unwrapRefType(toTy) == |
| fir::unwrapRefType(fir::unwrapPassByRefType(fromTy)) && |
| "element types expected to match")); |
| return create<fir::BoxAddrOp>(loc, toTy, val); |
| } |
| if (fir::isa_ref_type(fromTy) && mlir::isa<fir::BoxProcType>(toTy)) { |
| // Call is expecting a boxed procedure, not a reference to other data type. |
| // Convert the reference to a procedure and embox it. |
| mlir::Type procTy = mlir::cast<fir::BoxProcType>(toTy).getEleTy(); |
| mlir::Value proc = createConvert(loc, procTy, val); |
| return create<fir::EmboxProcOp>(loc, toTy, proc); |
| } |
| |
| // Legacy: remove when removing non HLFIR lowering path. |
| if (allowRebox) |
| if (((fir::isPolymorphicType(fromTy) && |
| (fir::isAllocatableType(fromTy) || fir::isPointerType(fromTy)) && |
| fir::isPolymorphicType(toTy)) || |
| (fir::isPolymorphicType(fromTy) && mlir::isa<fir::BoxType>(toTy))) && |
| !(fir::isUnlimitedPolymorphicType(fromTy) && fir::isAssumedType(toTy))) |
| return create<fir::ReboxOp>(loc, toTy, val, mlir::Value{}, |
| /*slice=*/mlir::Value{}); |
| |
| return createConvert(loc, toTy, val); |
| } |
| |
| mlir::Value fir::FirOpBuilder::createConvert(mlir::Location loc, |
| mlir::Type toTy, mlir::Value val) { |
| if (val.getType() != toTy) { |
| assert(!fir::isa_derived(toTy)); |
| return create<fir::ConvertOp>(loc, toTy, val); |
| } |
| return val; |
| } |
| |
| void fir::FirOpBuilder::createStoreWithConvert(mlir::Location loc, |
| mlir::Value val, |
| mlir::Value addr) { |
| mlir::Value cast = |
| createConvert(loc, fir::unwrapRefType(addr.getType()), val); |
| create<fir::StoreOp>(loc, cast, addr); |
| } |
| |
| mlir::Value fir::FirOpBuilder::loadIfRef(mlir::Location loc, mlir::Value val) { |
| if (fir::isa_ref_type(val.getType())) |
| return create<fir::LoadOp>(loc, val); |
| return val; |
| } |
| |
| fir::StringLitOp fir::FirOpBuilder::createStringLitOp(mlir::Location loc, |
| llvm::StringRef data) { |
| auto type = fir::CharacterType::get(getContext(), 1, data.size()); |
| auto strAttr = mlir::StringAttr::get(getContext(), data); |
| auto valTag = mlir::StringAttr::get(getContext(), fir::StringLitOp::value()); |
| mlir::NamedAttribute dataAttr(valTag, strAttr); |
| auto sizeTag = mlir::StringAttr::get(getContext(), fir::StringLitOp::size()); |
| mlir::NamedAttribute sizeAttr(sizeTag, getI64IntegerAttr(data.size())); |
| llvm::SmallVector<mlir::NamedAttribute> attrs{dataAttr, sizeAttr}; |
| return create<fir::StringLitOp>(loc, llvm::ArrayRef<mlir::Type>{type}, |
| std::nullopt, attrs); |
| } |
| |
| mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, |
| llvm::ArrayRef<mlir::Value> exts) { |
| return create<fir::ShapeOp>(loc, exts); |
| } |
| |
| mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, |
| llvm::ArrayRef<mlir::Value> shift, |
| llvm::ArrayRef<mlir::Value> exts) { |
| auto shapeType = fir::ShapeShiftType::get(getContext(), exts.size()); |
| llvm::SmallVector<mlir::Value> shapeArgs; |
| auto idxTy = getIndexType(); |
| for (auto [lbnd, ext] : llvm::zip(shift, exts)) { |
| auto lb = createConvert(loc, idxTy, lbnd); |
| shapeArgs.push_back(lb); |
| shapeArgs.push_back(ext); |
| } |
| return create<fir::ShapeShiftOp>(loc, shapeType, shapeArgs); |
| } |
| |
| mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, |
| const fir::AbstractArrayBox &arr) { |
| if (arr.lboundsAllOne()) |
| return genShape(loc, arr.getExtents()); |
| return genShape(loc, arr.getLBounds(), arr.getExtents()); |
| } |
| |
| mlir::Value fir::FirOpBuilder::genShift(mlir::Location loc, |
| llvm::ArrayRef<mlir::Value> shift) { |
| auto shiftType = fir::ShiftType::get(getContext(), shift.size()); |
| return create<fir::ShiftOp>(loc, shiftType, shift); |
| } |
| |
| mlir::Value fir::FirOpBuilder::createShape(mlir::Location loc, |
| const fir::ExtendedValue &exv) { |
| return exv.match( |
| [&](const fir::ArrayBoxValue &box) { return genShape(loc, box); }, |
| [&](const fir::CharArrayBoxValue &box) { return genShape(loc, box); }, |
| [&](const fir::BoxValue &box) -> mlir::Value { |
| if (!box.getLBounds().empty()) { |
| auto shiftType = |
| fir::ShiftType::get(getContext(), box.getLBounds().size()); |
| return create<fir::ShiftOp>(loc, shiftType, box.getLBounds()); |
| } |
| return {}; |
| }, |
| [&](const fir::MutableBoxValue &) -> mlir::Value { |
| // MutableBoxValue must be read into another category to work with them |
| // outside of allocation/assignment contexts. |
| fir::emitFatalError(loc, "createShape on MutableBoxValue"); |
| }, |
| [&](auto) -> mlir::Value { fir::emitFatalError(loc, "not an array"); }); |
| } |
| |
| mlir::Value fir::FirOpBuilder::createSlice(mlir::Location loc, |
| const fir::ExtendedValue &exv, |
| mlir::ValueRange triples, |
| mlir::ValueRange path) { |
| if (triples.empty()) { |
| // If there is no slicing by triple notation, then take the whole array. |
| auto fullShape = [&](const llvm::ArrayRef<mlir::Value> lbounds, |
| llvm::ArrayRef<mlir::Value> extents) -> mlir::Value { |
| llvm::SmallVector<mlir::Value> trips; |
| auto idxTy = getIndexType(); |
| auto one = createIntegerConstant(loc, idxTy, 1); |
| if (lbounds.empty()) { |
| for (auto v : extents) { |
| trips.push_back(one); |
| trips.push_back(v); |
| trips.push_back(one); |
| } |
| return create<fir::SliceOp>(loc, trips, path); |
| } |
| for (auto [lbnd, extent] : llvm::zip(lbounds, extents)) { |
| auto lb = createConvert(loc, idxTy, lbnd); |
| auto ext = createConvert(loc, idxTy, extent); |
| auto shift = create<mlir::arith::SubIOp>(loc, lb, one); |
| auto ub = create<mlir::arith::AddIOp>(loc, ext, shift); |
| trips.push_back(lb); |
| trips.push_back(ub); |
| trips.push_back(one); |
| } |
| return create<fir::SliceOp>(loc, trips, path); |
| }; |
| return exv.match( |
| [&](const fir::ArrayBoxValue &box) { |
| return fullShape(box.getLBounds(), box.getExtents()); |
| }, |
| [&](const fir::CharArrayBoxValue &box) { |
| return fullShape(box.getLBounds(), box.getExtents()); |
| }, |
| [&](const fir::BoxValue &box) { |
| auto extents = fir::factory::readExtents(*this, loc, box); |
| return fullShape(box.getLBounds(), extents); |
| }, |
| [&](const fir::MutableBoxValue &) -> mlir::Value { |
| // MutableBoxValue must be read into another category to work with |
| // them outside of allocation/assignment contexts. |
| fir::emitFatalError(loc, "createSlice on MutableBoxValue"); |
| }, |
| [&](auto) -> mlir::Value { fir::emitFatalError(loc, "not an array"); }); |
| } |
| return create<fir::SliceOp>(loc, triples, path); |
| } |
| |
| mlir::Value fir::FirOpBuilder::createBox(mlir::Location loc, |
| const fir::ExtendedValue &exv, |
| bool isPolymorphic, |
| bool isAssumedType) { |
| mlir::Value itemAddr = fir::getBase(exv); |
| if (mlir::isa<fir::BaseBoxType>(itemAddr.getType())) |
| return itemAddr; |
| auto elementType = fir::dyn_cast_ptrEleTy(itemAddr.getType()); |
| if (!elementType) { |
| mlir::emitError(loc, "internal: expected a memory reference type ") |
| << itemAddr.getType(); |
| llvm_unreachable("not a memory reference type"); |
| } |
| mlir::Type boxTy; |
| mlir::Value tdesc; |
| // Avoid to wrap a box/class with box/class. |
| if (mlir::isa<fir::BaseBoxType>(elementType)) { |
| boxTy = elementType; |
| } else { |
| boxTy = fir::BoxType::get(elementType); |
| if (isPolymorphic) { |
| elementType = fir::updateTypeForUnlimitedPolymorphic(elementType); |
| if (isAssumedType) |
| boxTy = fir::BoxType::get(elementType); |
| else |
| boxTy = fir::ClassType::get(elementType); |
| } |
| } |
| |
| return exv.match( |
| [&](const fir::ArrayBoxValue &box) -> mlir::Value { |
| mlir::Value empty; |
| mlir::ValueRange emptyRange; |
| mlir::Value s = createShape(loc, exv); |
| return create<fir::EmboxOp>(loc, boxTy, itemAddr, s, /*slice=*/empty, |
| /*typeparams=*/emptyRange, |
| isPolymorphic ? box.getSourceBox() : tdesc); |
| }, |
| [&](const fir::CharArrayBoxValue &box) -> mlir::Value { |
| mlir::Value s = createShape(loc, exv); |
| if (fir::factory::CharacterExprHelper::hasConstantLengthInType(exv)) |
| return create<fir::EmboxOp>(loc, boxTy, itemAddr, s); |
| |
| mlir::Value emptySlice; |
| llvm::SmallVector<mlir::Value> lenParams{box.getLen()}; |
| return create<fir::EmboxOp>(loc, boxTy, itemAddr, s, emptySlice, |
| lenParams); |
| }, |
| [&](const fir::CharBoxValue &box) -> mlir::Value { |
| if (fir::factory::CharacterExprHelper::hasConstantLengthInType(exv)) |
| return create<fir::EmboxOp>(loc, boxTy, itemAddr); |
| mlir::Value emptyShape, emptySlice; |
| llvm::SmallVector<mlir::Value> lenParams{box.getLen()}; |
| return create<fir::EmboxOp>(loc, boxTy, itemAddr, emptyShape, |
| emptySlice, lenParams); |
| }, |
| [&](const fir::MutableBoxValue &x) -> mlir::Value { |
| return create<fir::LoadOp>( |
| loc, fir::factory::getMutableIRBox(*this, loc, x)); |
| }, |
| [&](const fir::PolymorphicValue &p) -> mlir::Value { |
| mlir::Value empty; |
| mlir::ValueRange emptyRange; |
| return create<fir::EmboxOp>(loc, boxTy, itemAddr, empty, empty, |
| emptyRange, |
| isPolymorphic ? p.getSourceBox() : tdesc); |
| }, |
| [&](const auto &) -> mlir::Value { |
| mlir::Value empty; |
| mlir::ValueRange emptyRange; |
| return create<fir::EmboxOp>(loc, boxTy, itemAddr, empty, empty, |
| emptyRange, tdesc); |
| }); |
| } |
| |
| mlir::Value fir::FirOpBuilder::createBox(mlir::Location loc, mlir::Type boxType, |
| mlir::Value addr, mlir::Value shape, |
| mlir::Value slice, |
| llvm::ArrayRef<mlir::Value> lengths, |
| mlir::Value tdesc) { |
| mlir::Type valueOrSequenceType = fir::unwrapPassByRefType(boxType); |
| return create<fir::EmboxOp>( |
| loc, boxType, addr, shape, slice, |
| fir::factory::elideLengthsAlreadyInType(valueOrSequenceType, lengths), |
| tdesc); |
| } |
| |
| void fir::FirOpBuilder::dumpFunc() { getFunction().dump(); } |
| |
| static mlir::Value |
| genNullPointerComparison(fir::FirOpBuilder &builder, mlir::Location loc, |
| mlir::Value addr, |
| mlir::arith::CmpIPredicate condition) { |
| auto intPtrTy = builder.getIntPtrType(); |
| auto ptrToInt = builder.createConvert(loc, intPtrTy, addr); |
| auto c0 = builder.createIntegerConstant(loc, intPtrTy, 0); |
| return builder.create<mlir::arith::CmpIOp>(loc, condition, ptrToInt, c0); |
| } |
| |
| mlir::Value fir::FirOpBuilder::genIsNotNullAddr(mlir::Location loc, |
| mlir::Value addr) { |
| return genNullPointerComparison(*this, loc, addr, |
| mlir::arith::CmpIPredicate::ne); |
| } |
| |
| mlir::Value fir::FirOpBuilder::genIsNullAddr(mlir::Location loc, |
| mlir::Value addr) { |
| return genNullPointerComparison(*this, loc, addr, |
| mlir::arith::CmpIPredicate::eq); |
| } |
| |
| mlir::Value fir::FirOpBuilder::genExtentFromTriplet(mlir::Location loc, |
| mlir::Value lb, |
| mlir::Value ub, |
| mlir::Value step, |
| mlir::Type type) { |
| auto zero = createIntegerConstant(loc, type, 0); |
| lb = createConvert(loc, type, lb); |
| ub = createConvert(loc, type, ub); |
| step = createConvert(loc, type, step); |
| auto diff = create<mlir::arith::SubIOp>(loc, ub, lb); |
| auto add = create<mlir::arith::AddIOp>(loc, diff, step); |
| auto div = create<mlir::arith::DivSIOp>(loc, add, step); |
| auto cmp = create<mlir::arith::CmpIOp>(loc, mlir::arith::CmpIPredicate::sgt, |
| div, zero); |
| return create<mlir::arith::SelectOp>(loc, cmp, div, zero); |
| } |
| |
| mlir::Value fir::FirOpBuilder::genAbsentOp(mlir::Location loc, |
| mlir::Type argTy) { |
| if (!fir::isCharacterProcedureTuple(argTy)) |
| return create<fir::AbsentOp>(loc, argTy); |
| |
| auto boxProc = |
| create<fir::AbsentOp>(loc, mlir::cast<mlir::TupleType>(argTy).getType(0)); |
| mlir::Value charLen = create<fir::UndefOp>(loc, getCharacterLengthType()); |
| return fir::factory::createCharacterProcedureTuple(*this, loc, argTy, boxProc, |
| charLen); |
| } |
| |
| void fir::FirOpBuilder::setCommonAttributes(mlir::Operation *op) const { |
| auto fmi = mlir::dyn_cast<mlir::arith::ArithFastMathInterface>(*op); |
| if (!fmi) |
| return; |
| // TODO: use fmi.setFastMathFlagsAttr() after D137114 is merged. |
| // For now set the attribute by the name. |
| llvm::StringRef arithFMFAttrName = fmi.getFastMathAttrName(); |
| if (fastMathFlags != mlir::arith::FastMathFlags::none) |
| op->setAttr(arithFMFAttrName, mlir::arith::FastMathFlagsAttr::get( |
| op->getContext(), fastMathFlags)); |
| } |
| |
| void fir::FirOpBuilder::setFastMathFlags( |
| Fortran::common::MathOptionsBase options) { |
| mlir::arith::FastMathFlags arithFMF{}; |
| if (options.getFPContractEnabled()) { |
| arithFMF = arithFMF | mlir::arith::FastMathFlags::contract; |
| } |
| if (options.getNoHonorInfs()) { |
| arithFMF = arithFMF | mlir::arith::FastMathFlags::ninf; |
| } |
| if (options.getNoHonorNaNs()) { |
| arithFMF = arithFMF | mlir::arith::FastMathFlags::nnan; |
| } |
| if (options.getApproxFunc()) { |
| arithFMF = arithFMF | mlir::arith::FastMathFlags::afn; |
| } |
| if (options.getNoSignedZeros()) { |
| arithFMF = arithFMF | mlir::arith::FastMathFlags::nsz; |
| } |
| if (options.getAssociativeMath()) { |
| arithFMF = arithFMF | mlir::arith::FastMathFlags::reassoc; |
| } |
| if (options.getReciprocalMath()) { |
| arithFMF = arithFMF | mlir::arith::FastMathFlags::arcp; |
| } |
| setFastMathFlags(arithFMF); |
| } |
| |
| //===--------------------------------------------------------------------===// |
| // ExtendedValue inquiry helper implementation |
| //===--------------------------------------------------------------------===// |
| |
| mlir::Value fir::factory::readCharLen(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| const fir::ExtendedValue &box) { |
| return box.match( |
| [&](const fir::CharBoxValue &x) -> mlir::Value { return x.getLen(); }, |
| [&](const fir::CharArrayBoxValue &x) -> mlir::Value { |
| return x.getLen(); |
| }, |
| [&](const fir::BoxValue &x) -> mlir::Value { |
| assert(x.isCharacter()); |
| if (!x.getExplicitParameters().empty()) |
| return x.getExplicitParameters()[0]; |
| return fir::factory::CharacterExprHelper{builder, loc} |
| .readLengthFromBox(x.getAddr()); |
| }, |
| [&](const fir::MutableBoxValue &x) -> mlir::Value { |
| return readCharLen(builder, loc, |
| fir::factory::genMutableBoxRead(builder, loc, x)); |
| }, |
| [&](const auto &) -> mlir::Value { |
| fir::emitFatalError( |
| loc, "Character length inquiry on a non-character entity"); |
| }); |
| } |
| |
| mlir::Value fir::factory::readExtent(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| const fir::ExtendedValue &box, |
| unsigned dim) { |
| assert(box.rank() > dim); |
| return box.match( |
| [&](const fir::ArrayBoxValue &x) -> mlir::Value { |
| return x.getExtents()[dim]; |
| }, |
| [&](const fir::CharArrayBoxValue &x) -> mlir::Value { |
| return x.getExtents()[dim]; |
| }, |
| [&](const fir::BoxValue &x) -> mlir::Value { |
| if (!x.getExplicitExtents().empty()) |
| return x.getExplicitExtents()[dim]; |
| auto idxTy = builder.getIndexType(); |
| auto dimVal = builder.createIntegerConstant(loc, idxTy, dim); |
| return builder |
| .create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, x.getAddr(), |
| dimVal) |
| .getResult(1); |
| }, |
| [&](const fir::MutableBoxValue &x) -> mlir::Value { |
| return readExtent(builder, loc, |
| fir::factory::genMutableBoxRead(builder, loc, x), |
| dim); |
| }, |
| [&](const auto &) -> mlir::Value { |
| fir::emitFatalError(loc, "extent inquiry on scalar"); |
| }); |
| } |
| |
| mlir::Value fir::factory::readLowerBound(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| const fir::ExtendedValue &box, |
| unsigned dim, |
| mlir::Value defaultValue) { |
| assert(box.rank() > dim); |
| auto lb = box.match( |
| [&](const fir::ArrayBoxValue &x) -> mlir::Value { |
| return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim]; |
| }, |
| [&](const fir::CharArrayBoxValue &x) -> mlir::Value { |
| return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim]; |
| }, |
| [&](const fir::BoxValue &x) -> mlir::Value { |
| return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim]; |
| }, |
| [&](const fir::MutableBoxValue &x) -> mlir::Value { |
| return readLowerBound(builder, loc, |
| fir::factory::genMutableBoxRead(builder, loc, x), |
| dim, defaultValue); |
| }, |
| [&](const auto &) -> mlir::Value { |
| fir::emitFatalError(loc, "lower bound inquiry on scalar"); |
| }); |
| if (lb) |
| return lb; |
| return defaultValue; |
| } |
| |
| llvm::SmallVector<mlir::Value> |
| fir::factory::readExtents(fir::FirOpBuilder &builder, mlir::Location loc, |
| const fir::BoxValue &box) { |
| llvm::SmallVector<mlir::Value> result; |
| auto explicitExtents = box.getExplicitExtents(); |
| if (!explicitExtents.empty()) { |
| result.append(explicitExtents.begin(), explicitExtents.end()); |
| return result; |
| } |
| auto rank = box.rank(); |
| auto idxTy = builder.getIndexType(); |
| for (decltype(rank) dim = 0; dim < rank; ++dim) { |
| auto dimVal = builder.createIntegerConstant(loc, idxTy, dim); |
| auto dimInfo = builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, |
| box.getAddr(), dimVal); |
| result.emplace_back(dimInfo.getResult(1)); |
| } |
| return result; |
| } |
| |
| llvm::SmallVector<mlir::Value> |
| fir::factory::getExtents(mlir::Location loc, fir::FirOpBuilder &builder, |
| const fir::ExtendedValue &box) { |
| return box.match( |
| [&](const fir::ArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> { |
| return {x.getExtents().begin(), x.getExtents().end()}; |
| }, |
| [&](const fir::CharArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> { |
| return {x.getExtents().begin(), x.getExtents().end()}; |
| }, |
| [&](const fir::BoxValue &x) -> llvm::SmallVector<mlir::Value> { |
| return fir::factory::readExtents(builder, loc, x); |
| }, |
| [&](const fir::MutableBoxValue &x) -> llvm::SmallVector<mlir::Value> { |
| auto load = fir::factory::genMutableBoxRead(builder, loc, x); |
| return fir::factory::getExtents(loc, builder, load); |
| }, |
| [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; }); |
| } |
| |
| fir::ExtendedValue fir::factory::readBoxValue(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| const fir::BoxValue &box) { |
| assert(!box.hasAssumedRank() && |
| "cannot read unlimited polymorphic or assumed rank fir.box"); |
| auto addr = |
| builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr()); |
| if (box.isCharacter()) { |
| auto len = fir::factory::readCharLen(builder, loc, box); |
| if (box.rank() == 0) |
| return fir::CharBoxValue(addr, len); |
| return fir::CharArrayBoxValue(addr, len, |
| fir::factory::readExtents(builder, loc, box), |
| box.getLBounds()); |
| } |
| if (box.isDerivedWithLenParameters()) |
| TODO(loc, "read fir.box with length parameters"); |
| mlir::Value sourceBox; |
| if (box.isPolymorphic()) |
| sourceBox = box.getAddr(); |
| if (box.isPolymorphic() && box.rank() == 0) |
| return fir::PolymorphicValue(addr, sourceBox); |
| if (box.rank() == 0) |
| return addr; |
| return fir::ArrayBoxValue(addr, fir::factory::readExtents(builder, loc, box), |
| box.getLBounds(), sourceBox); |
| } |
| |
| llvm::SmallVector<mlir::Value> |
| fir::factory::getNonDefaultLowerBounds(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| const fir::ExtendedValue &exv) { |
| return exv.match( |
| [&](const fir::ArrayBoxValue &array) -> llvm::SmallVector<mlir::Value> { |
| return {array.getLBounds().begin(), array.getLBounds().end()}; |
| }, |
| [&](const fir::CharArrayBoxValue &array) |
| -> llvm::SmallVector<mlir::Value> { |
| return {array.getLBounds().begin(), array.getLBounds().end()}; |
| }, |
| [&](const fir::BoxValue &box) -> llvm::SmallVector<mlir::Value> { |
| return {box.getLBounds().begin(), box.getLBounds().end()}; |
| }, |
| [&](const fir::MutableBoxValue &box) -> llvm::SmallVector<mlir::Value> { |
| auto load = fir::factory::genMutableBoxRead(builder, loc, box); |
| return fir::factory::getNonDefaultLowerBounds(builder, loc, load); |
| }, |
| [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; }); |
| } |
| |
| llvm::SmallVector<mlir::Value> |
| fir::factory::getNonDeferredLenParams(const fir::ExtendedValue &exv) { |
| return exv.match( |
| [&](const fir::CharArrayBoxValue &character) |
| -> llvm::SmallVector<mlir::Value> { return {character.getLen()}; }, |
| [&](const fir::CharBoxValue &character) |
| -> llvm::SmallVector<mlir::Value> { return {character.getLen()}; }, |
| [&](const fir::MutableBoxValue &box) -> llvm::SmallVector<mlir::Value> { |
| return {box.nonDeferredLenParams().begin(), |
| box.nonDeferredLenParams().end()}; |
| }, |
| [&](const fir::BoxValue &box) -> llvm::SmallVector<mlir::Value> { |
| return {box.getExplicitParameters().begin(), |
| box.getExplicitParameters().end()}; |
| }, |
| [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; }); |
| } |
| |
| // If valTy is a box type, then we need to extract the type parameters from |
| // the box value. |
| static llvm::SmallVector<mlir::Value> getFromBox(mlir::Location loc, |
| fir::FirOpBuilder &builder, |
| mlir::Type valTy, |
| mlir::Value boxVal) { |
| if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(valTy)) { |
| auto eleTy = fir::unwrapAllRefAndSeqType(boxTy.getEleTy()); |
| if (auto recTy = mlir::dyn_cast<fir::RecordType>(eleTy)) { |
| if (recTy.getNumLenParams() > 0) { |
| // Walk each type parameter in the record and get the value. |
| TODO(loc, "generate code to get LEN type parameters"); |
| } |
| } else if (auto charTy = mlir::dyn_cast<fir::CharacterType>(eleTy)) { |
| if (charTy.hasDynamicLen()) { |
| auto idxTy = builder.getIndexType(); |
| auto eleSz = builder.create<fir::BoxEleSizeOp>(loc, idxTy, boxVal); |
| auto kindBytes = |
| builder.getKindMap().getCharacterBitsize(charTy.getFKind()) / 8; |
| mlir::Value charSz = |
| builder.createIntegerConstant(loc, idxTy, kindBytes); |
| mlir::Value len = |
| builder.create<mlir::arith::DivSIOp>(loc, eleSz, charSz); |
| return {len}; |
| } |
| } |
| } |
| return {}; |
| } |
| |
| // fir::getTypeParams() will get the type parameters from the extended value. |
| // When the extended value is a BoxValue or MutableBoxValue, it may be necessary |
| // to generate code, so this factory function handles those cases. |
| // TODO: fix the inverted type tests, etc. |
| llvm::SmallVector<mlir::Value> |
| fir::factory::getTypeParams(mlir::Location loc, fir::FirOpBuilder &builder, |
| const fir::ExtendedValue &exv) { |
| auto handleBoxed = [&](const auto &box) -> llvm::SmallVector<mlir::Value> { |
| if (box.isCharacter()) |
| return {fir::factory::readCharLen(builder, loc, exv)}; |
| if (box.isDerivedWithLenParameters()) { |
| // This should generate code to read the type parameters from the box. |
| // This requires some consideration however as MutableBoxValues need to be |
| // in a sane state to be provide the correct values. |
| TODO(loc, "derived type with type parameters"); |
| } |
| return {}; |
| }; |
| // Intentionally reuse the original code path to get type parameters for the |
| // cases that were supported rather than introduce a new path. |
| return exv.match( |
| [&](const fir::BoxValue &box) { return handleBoxed(box); }, |
| [&](const fir::MutableBoxValue &box) { return handleBoxed(box); }, |
| [&](const auto &) { return fir::getTypeParams(exv); }); |
| } |
| |
| llvm::SmallVector<mlir::Value> |
| fir::factory::getTypeParams(mlir::Location loc, fir::FirOpBuilder &builder, |
| fir::ArrayLoadOp load) { |
| mlir::Type memTy = load.getMemref().getType(); |
| if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(memTy)) |
| return getFromBox(loc, builder, boxTy, load.getMemref()); |
| return load.getTypeparams(); |
| } |
| |
| std::string fir::factory::uniqueCGIdent(llvm::StringRef prefix, |
| llvm::StringRef name) { |
| // For "long" identifiers use a hash value |
| if (name.size() > nameLengthHashSize) { |
| llvm::MD5 hash; |
| hash.update(name); |
| llvm::MD5::MD5Result result; |
| hash.final(result); |
| llvm::SmallString<32> str; |
| llvm::MD5::stringifyResult(result, str); |
| std::string hashName = prefix.str(); |
| hashName.append("X").append(str.c_str()); |
| return fir::NameUniquer::doGenerated(hashName); |
| } |
| // "Short" identifiers use a reversible hex string |
| std::string nm = prefix.str(); |
| return fir::NameUniquer::doGenerated( |
| nm.append("X").append(llvm::toHex(name))); |
| } |
| |
| mlir::Value fir::factory::locationToFilename(fir::FirOpBuilder &builder, |
| mlir::Location loc) { |
| if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(loc)) { |
| // must be encoded as asciiz, C string |
| auto fn = flc.getFilename().str() + '\0'; |
| return fir::getBase(createStringLiteral(builder, loc, fn)); |
| } |
| return builder.createNullConstant(loc); |
| } |
| |
| mlir::Value fir::factory::locationToLineNo(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| mlir::Type type) { |
| if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(loc)) |
| return builder.createIntegerConstant(loc, type, flc.getLine()); |
| return builder.createIntegerConstant(loc, type, 0); |
| } |
| |
| fir::ExtendedValue fir::factory::createStringLiteral(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| llvm::StringRef str) { |
| std::string globalName = fir::factory::uniqueCGIdent("cl", str); |
| auto type = fir::CharacterType::get(builder.getContext(), 1, str.size()); |
| auto global = builder.getNamedGlobal(globalName); |
| if (!global) |
| global = builder.createGlobalConstant( |
| loc, type, globalName, |
| [&](fir::FirOpBuilder &builder) { |
| auto stringLitOp = builder.createStringLitOp(loc, str); |
| builder.create<fir::HasValueOp>(loc, stringLitOp); |
| }, |
| builder.createLinkOnceLinkage()); |
| auto addr = builder.create<fir::AddrOfOp>(loc, global.resultType(), |
| global.getSymbol()); |
| auto len = builder.createIntegerConstant( |
| loc, builder.getCharacterLengthType(), str.size()); |
| return fir::CharBoxValue{addr, len}; |
| } |
| |
| llvm::SmallVector<mlir::Value> |
| fir::factory::createExtents(fir::FirOpBuilder &builder, mlir::Location loc, |
| fir::SequenceType seqTy) { |
| llvm::SmallVector<mlir::Value> extents; |
| auto idxTy = builder.getIndexType(); |
| for (auto ext : seqTy.getShape()) |
| extents.emplace_back( |
| ext == fir::SequenceType::getUnknownExtent() |
| ? builder.create<fir::UndefOp>(loc, idxTy).getResult() |
| : builder.createIntegerConstant(loc, idxTy, ext)); |
| return extents; |
| } |
| |
| // FIXME: This needs some work. To correctly determine the extended value of a |
| // component, one needs the base object, its type, and its type parameters. (An |
| // alternative would be to provide an already computed address of the final |
| // component rather than the base object's address, the point being the result |
| // will require the address of the final component to create the extended |
| // value.) One further needs the full path of components being applied. One |
| // needs to apply type-based expressions to type parameters along this said |
| // path. (See applyPathToType for a type-only derivation.) Finally, one needs to |
| // compose the extended value of the terminal component, including all of its |
| // parameters: array lower bounds expressions, extents, type parameters, etc. |
| // Any of these properties may be deferred until runtime in Fortran. This |
| // operation may therefore generate a sizeable block of IR, including calls to |
| // type-based helper functions, so caching the result of this operation in the |
| // client would be advised as well. |
| fir::ExtendedValue fir::factory::componentToExtendedValue( |
| fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value component) { |
| auto fieldTy = component.getType(); |
| if (auto ty = fir::dyn_cast_ptrEleTy(fieldTy)) |
| fieldTy = ty; |
| if (mlir::isa<fir::BaseBoxType>(fieldTy)) { |
| llvm::SmallVector<mlir::Value> nonDeferredTypeParams; |
| auto eleTy = fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(fieldTy)); |
| if (auto charTy = mlir::dyn_cast<fir::CharacterType>(eleTy)) { |
| auto lenTy = builder.getCharacterLengthType(); |
| if (charTy.hasConstantLen()) |
| nonDeferredTypeParams.emplace_back( |
| builder.createIntegerConstant(loc, lenTy, charTy.getLen())); |
| // TODO: Starting, F2003, the dynamic character length might be dependent |
| // on a PDT length parameter. There is no way to make a difference with |
| // deferred length here yet. |
| } |
| if (auto recTy = mlir::dyn_cast<fir::RecordType>(eleTy)) |
| if (recTy.getNumLenParams() > 0) |
| TODO(loc, "allocatable and pointer components non deferred length " |
| "parameters"); |
| |
| return fir::MutableBoxValue(component, nonDeferredTypeParams, |
| /*mutableProperties=*/{}); |
| } |
| llvm::SmallVector<mlir::Value> extents; |
| if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(fieldTy)) { |
| fieldTy = seqTy.getEleTy(); |
| auto idxTy = builder.getIndexType(); |
| for (auto extent : seqTy.getShape()) { |
| if (extent == fir::SequenceType::getUnknownExtent()) |
| TODO(loc, "array component shape depending on length parameters"); |
| extents.emplace_back(builder.createIntegerConstant(loc, idxTy, extent)); |
| } |
| } |
| if (auto charTy = mlir::dyn_cast<fir::CharacterType>(fieldTy)) { |
| auto cstLen = charTy.getLen(); |
| if (cstLen == fir::CharacterType::unknownLen()) |
| TODO(loc, "get character component length from length type parameters"); |
| auto len = builder.createIntegerConstant( |
| loc, builder.getCharacterLengthType(), cstLen); |
| if (!extents.empty()) |
| return fir::CharArrayBoxValue{component, len, extents}; |
| return fir::CharBoxValue{component, len}; |
| } |
| if (auto recordTy = mlir::dyn_cast<fir::RecordType>(fieldTy)) |
| if (recordTy.getNumLenParams() != 0) |
| TODO(loc, |
| "lower component ref that is a derived type with length parameter"); |
| if (!extents.empty()) |
| return fir::ArrayBoxValue{component, extents}; |
| return component; |
| } |
| |
| fir::ExtendedValue fir::factory::arrayElementToExtendedValue( |
| fir::FirOpBuilder &builder, mlir::Location loc, |
| const fir::ExtendedValue &array, mlir::Value element) { |
| return array.match( |
| [&](const fir::CharBoxValue &cb) -> fir::ExtendedValue { |
| return cb.clone(element); |
| }, |
| [&](const fir::CharArrayBoxValue &bv) -> fir::ExtendedValue { |
| return bv.cloneElement(element); |
| }, |
| [&](const fir::BoxValue &box) -> fir::ExtendedValue { |
| if (box.isCharacter()) { |
| auto len = fir::factory::readCharLen(builder, loc, box); |
| return fir::CharBoxValue{element, len}; |
| } |
| if (box.isDerivedWithLenParameters()) |
| TODO(loc, "get length parameters from derived type BoxValue"); |
| if (box.isPolymorphic()) { |
| return fir::PolymorphicValue(element, fir::getBase(box)); |
| } |
| return element; |
| }, |
| [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { |
| if (box.getSourceBox()) |
| return fir::PolymorphicValue(element, box.getSourceBox()); |
| return element; |
| }, |
| [&](const auto &) -> fir::ExtendedValue { return element; }); |
| } |
| |
| fir::ExtendedValue fir::factory::arraySectionElementToExtendedValue( |
| fir::FirOpBuilder &builder, mlir::Location loc, |
| const fir::ExtendedValue &array, mlir::Value element, mlir::Value slice) { |
| if (!slice) |
| return arrayElementToExtendedValue(builder, loc, array, element); |
| auto sliceOp = mlir::dyn_cast_or_null<fir::SliceOp>(slice.getDefiningOp()); |
| assert(sliceOp && "slice must be a sliceOp"); |
| if (sliceOp.getFields().empty()) |
| return arrayElementToExtendedValue(builder, loc, array, element); |
| // For F95, using componentToExtendedValue will work, but when PDTs are |
| // lowered. It will be required to go down the slice to propagate the length |
| // parameters. |
| return fir::factory::componentToExtendedValue(builder, loc, element); |
| } |
| |
| void fir::factory::genScalarAssignment(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| const fir::ExtendedValue &lhs, |
| const fir::ExtendedValue &rhs, |
| bool needFinalization, |
| bool isTemporaryLHS) { |
| assert(lhs.rank() == 0 && rhs.rank() == 0 && "must be scalars"); |
| auto type = fir::unwrapSequenceType( |
| fir::unwrapPassByRefType(fir::getBase(lhs).getType())); |
| if (mlir::isa<fir::CharacterType>(type)) { |
| const fir::CharBoxValue *toChar = lhs.getCharBox(); |
| const fir::CharBoxValue *fromChar = rhs.getCharBox(); |
| assert(toChar && fromChar); |
| fir::factory::CharacterExprHelper helper{builder, loc}; |
| helper.createAssign(fir::ExtendedValue{*toChar}, |
| fir::ExtendedValue{*fromChar}); |
| } else if (mlir::isa<fir::RecordType>(type)) { |
| fir::factory::genRecordAssignment(builder, loc, lhs, rhs, needFinalization, |
| isTemporaryLHS); |
| } else { |
| assert(!fir::hasDynamicSize(type)); |
| auto rhsVal = fir::getBase(rhs); |
| if (fir::isa_ref_type(rhsVal.getType())) |
| rhsVal = builder.create<fir::LoadOp>(loc, rhsVal); |
| mlir::Value lhsAddr = fir::getBase(lhs); |
| rhsVal = builder.createConvert(loc, fir::unwrapRefType(lhsAddr.getType()), |
| rhsVal); |
| builder.create<fir::StoreOp>(loc, rhsVal, lhsAddr); |
| } |
| } |
| |
| static void genComponentByComponentAssignment(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| const fir::ExtendedValue &lhs, |
| const fir::ExtendedValue &rhs, |
| bool isTemporaryLHS) { |
| auto lbaseType = fir::unwrapPassByRefType(fir::getBase(lhs).getType()); |
| auto lhsType = mlir::dyn_cast<fir::RecordType>(lbaseType); |
| assert(lhsType && "lhs must be a scalar record type"); |
| auto rbaseType = fir::unwrapPassByRefType(fir::getBase(rhs).getType()); |
| auto rhsType = mlir::dyn_cast<fir::RecordType>(rbaseType); |
| assert(rhsType && "rhs must be a scalar record type"); |
| auto fieldIndexType = fir::FieldType::get(lhsType.getContext()); |
| for (auto [lhsPair, rhsPair] : |
| llvm::zip(lhsType.getTypeList(), rhsType.getTypeList())) { |
| auto &[lFieldName, lFieldTy] = lhsPair; |
| auto &[rFieldName, rFieldTy] = rhsPair; |
| assert(!fir::hasDynamicSize(lFieldTy) && !fir::hasDynamicSize(rFieldTy)); |
| mlir::Value rField = builder.create<fir::FieldIndexOp>( |
| loc, fieldIndexType, rFieldName, rhsType, fir::getTypeParams(rhs)); |
| auto rFieldRefType = builder.getRefType(rFieldTy); |
| mlir::Value fromCoor = builder.create<fir::CoordinateOp>( |
| loc, rFieldRefType, fir::getBase(rhs), rField); |
| mlir::Value field = builder.create<fir::FieldIndexOp>( |
| loc, fieldIndexType, lFieldName, lhsType, fir::getTypeParams(lhs)); |
| auto fieldRefType = builder.getRefType(lFieldTy); |
| mlir::Value toCoor = builder.create<fir::CoordinateOp>( |
| loc, fieldRefType, fir::getBase(lhs), field); |
| std::optional<fir::DoLoopOp> outerLoop; |
| if (auto sequenceType = mlir::dyn_cast<fir::SequenceType>(lFieldTy)) { |
| // Create loops to assign array components elements by elements. |
| // Note that, since these are components, they either do not overlap, |
| // or are the same and exactly overlap. They also have compile time |
| // constant shapes. |
| mlir::Type idxTy = builder.getIndexType(); |
| llvm::SmallVector<mlir::Value> indices; |
| mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0); |
| mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); |
| for (auto extent : llvm::reverse(sequenceType.getShape())) { |
| // TODO: add zero size test ! |
| mlir::Value ub = builder.createIntegerConstant(loc, idxTy, extent - 1); |
| auto loop = builder.create<fir::DoLoopOp>(loc, zero, ub, one); |
| if (!outerLoop) |
| outerLoop = loop; |
| indices.push_back(loop.getInductionVar()); |
| builder.setInsertionPointToStart(loop.getBody()); |
| } |
| // Set indices in column-major order. |
| std::reverse(indices.begin(), indices.end()); |
| auto elementRefType = builder.getRefType(sequenceType.getEleTy()); |
| toCoor = builder.create<fir::CoordinateOp>(loc, elementRefType, toCoor, |
| indices); |
| fromCoor = builder.create<fir::CoordinateOp>(loc, elementRefType, |
| fromCoor, indices); |
| } |
| if (auto fieldEleTy = fir::unwrapSequenceType(lFieldTy); |
| mlir::isa<fir::BaseBoxType>(fieldEleTy)) { |
| assert(mlir::isa<fir::PointerType>( |
| mlir::cast<fir::BaseBoxType>(fieldEleTy).getEleTy()) && |
| "allocatable members require deep copy"); |
| auto fromPointerValue = builder.create<fir::LoadOp>(loc, fromCoor); |
| auto castTo = builder.createConvert(loc, fieldEleTy, fromPointerValue); |
| builder.create<fir::StoreOp>(loc, castTo, toCoor); |
| } else { |
| auto from = |
| fir::factory::componentToExtendedValue(builder, loc, fromCoor); |
| auto to = fir::factory::componentToExtendedValue(builder, loc, toCoor); |
| // If LHS finalization is needed it is expected to be done |
| // for the parent record, so that component-by-component |
| // assignments may avoid finalization calls. |
| fir::factory::genScalarAssignment(builder, loc, to, from, |
| /*needFinalization=*/false, |
| isTemporaryLHS); |
| } |
| if (outerLoop) |
| builder.setInsertionPointAfter(*outerLoop); |
| } |
| } |
| |
| /// Can the assignment of this record type be implement with a simple memory |
| /// copy (it requires no deep copy or user defined assignment of components )? |
| static bool recordTypeCanBeMemCopied(fir::RecordType recordType) { |
| if (fir::hasDynamicSize(recordType)) |
| return false; |
| for (auto [_, fieldType] : recordType.getTypeList()) { |
| // Derived type component may have user assignment (so far, we cannot tell |
| // in FIR, so assume it is always the case, TODO: get the actual info). |
| if (mlir::isa<fir::RecordType>(fir::unwrapSequenceType(fieldType))) |
| return false; |
| // Allocatable components need deep copy. |
| if (auto boxType = mlir::dyn_cast<fir::BaseBoxType>(fieldType)) |
| if (mlir::isa<fir::HeapType>(boxType.getEleTy())) |
| return false; |
| } |
| // Constant size components without user defined assignment and pointers can |
| // be memcopied. |
| return true; |
| } |
| |
| static bool mayHaveFinalizer(fir::RecordType recordType, |
| fir::FirOpBuilder &builder) { |
| if (auto typeInfo = builder.getModule().lookupSymbol<fir::TypeInfoOp>( |
| recordType.getName())) |
| return !typeInfo.getNoFinal(); |
| // No info, be pessimistic. |
| return true; |
| } |
| |
| void fir::factory::genRecordAssignment(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| const fir::ExtendedValue &lhs, |
| const fir::ExtendedValue &rhs, |
| bool needFinalization, |
| bool isTemporaryLHS) { |
| assert(lhs.rank() == 0 && rhs.rank() == 0 && "assume scalar assignment"); |
| auto baseTy = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(lhs).getType()); |
| assert(baseTy && "must be a memory type"); |
| // Box operands may be polymorphic, it is not entirely clear from 10.2.1.3 |
| // if the assignment is performed on the dynamic of declared type. Use the |
| // runtime assuming it is performed on the dynamic type. |
| bool hasBoxOperands = |
| mlir::isa<fir::BaseBoxType>(fir::getBase(lhs).getType()) || |
| mlir::isa<fir::BaseBoxType>(fir::getBase(rhs).getType()); |
| auto recTy = mlir::dyn_cast<fir::RecordType>(baseTy); |
| assert(recTy && "must be a record type"); |
| if ((needFinalization && mayHaveFinalizer(recTy, builder)) || |
| hasBoxOperands || !recordTypeCanBeMemCopied(recTy)) { |
| auto to = fir::getBase(builder.createBox(loc, lhs)); |
| auto from = fir::getBase(builder.createBox(loc, rhs)); |
| // The runtime entry point may modify the LHS descriptor if it is |
| // an allocatable. Allocatable assignment is handle elsewhere in lowering, |
| // so just create a fir.ref<fir.box<>> from the fir.box to comply with the |
| // runtime interface, but assume the fir.box is unchanged. |
| // TODO: does this holds true with polymorphic entities ? |
| auto toMutableBox = builder.createTemporary(loc, to.getType()); |
| builder.create<fir::StoreOp>(loc, to, toMutableBox); |
| if (isTemporaryLHS) |
| fir::runtime::genAssignTemporary(builder, loc, toMutableBox, from); |
| else |
| fir::runtime::genAssign(builder, loc, toMutableBox, from); |
| return; |
| } |
| |
| // Otherwise, the derived type has compile time constant size and for which |
| // the component by component assignment can be replaced by a memory copy. |
| // Since we do not know the size of the derived type in lowering, do a |
| // component by component assignment. Note that a single fir.load/fir.store |
| // could be used on "small" record types, but as the type size grows, this |
| // leads to issues in LLVM (long compile times, long IR files, and even |
| // asserts at some point). Since there is no good size boundary, just always |
| // use component by component assignment here. |
| genComponentByComponentAssignment(builder, loc, lhs, rhs, isTemporaryLHS); |
| } |
| |
| mlir::TupleType |
| fir::factory::getRaggedArrayHeaderType(fir::FirOpBuilder &builder) { |
| mlir::IntegerType i64Ty = builder.getIntegerType(64); |
| auto arrTy = fir::SequenceType::get(builder.getIntegerType(8), 1); |
| auto buffTy = fir::HeapType::get(arrTy); |
| auto extTy = fir::SequenceType::get(i64Ty, 1); |
| auto shTy = fir::HeapType::get(extTy); |
| return mlir::TupleType::get(builder.getContext(), {i64Ty, buffTy, shTy}); |
| } |
| |
| mlir::Value fir::factory::genLenOfCharacter( |
| fir::FirOpBuilder &builder, mlir::Location loc, fir::ArrayLoadOp arrLoad, |
| llvm::ArrayRef<mlir::Value> path, llvm::ArrayRef<mlir::Value> substring) { |
| llvm::SmallVector<mlir::Value> typeParams(arrLoad.getTypeparams()); |
| return genLenOfCharacter(builder, loc, |
| mlir::cast<fir::SequenceType>(arrLoad.getType()), |
| arrLoad.getMemref(), typeParams, path, substring); |
| } |
| |
| mlir::Value fir::factory::genLenOfCharacter( |
| fir::FirOpBuilder &builder, mlir::Location loc, fir::SequenceType seqTy, |
| mlir::Value memref, llvm::ArrayRef<mlir::Value> typeParams, |
| llvm::ArrayRef<mlir::Value> path, llvm::ArrayRef<mlir::Value> substring) { |
| auto idxTy = builder.getIndexType(); |
| auto zero = builder.createIntegerConstant(loc, idxTy, 0); |
| auto saturatedDiff = [&](mlir::Value lower, mlir::Value upper) { |
| auto diff = builder.create<mlir::arith::SubIOp>(loc, upper, lower); |
| auto one = builder.createIntegerConstant(loc, idxTy, 1); |
| auto size = builder.create<mlir::arith::AddIOp>(loc, diff, one); |
| auto cmp = builder.create<mlir::arith::CmpIOp>( |
| loc, mlir::arith::CmpIPredicate::sgt, size, zero); |
| return builder.create<mlir::arith::SelectOp>(loc, cmp, size, zero); |
| }; |
| if (substring.size() == 2) { |
| auto upper = builder.createConvert(loc, idxTy, substring.back()); |
| auto lower = builder.createConvert(loc, idxTy, substring.front()); |
| return saturatedDiff(lower, upper); |
| } |
| auto lower = zero; |
| if (substring.size() == 1) |
| lower = builder.createConvert(loc, idxTy, substring.front()); |
| auto eleTy = fir::applyPathToType(seqTy, path); |
| if (!fir::hasDynamicSize(eleTy)) { |
| if (auto charTy = mlir::dyn_cast<fir::CharacterType>(eleTy)) { |
| // Use LEN from the type. |
| return builder.createIntegerConstant(loc, idxTy, charTy.getLen()); |
| } |
| // Do we need to support !fir.array<!fir.char<k,n>>? |
| fir::emitFatalError(loc, |
| "application of path did not result in a !fir.char"); |
| } |
| if (fir::isa_box_type(memref.getType())) { |
| if (mlir::isa<fir::BoxCharType>(memref.getType())) |
| return builder.create<fir::BoxCharLenOp>(loc, idxTy, memref); |
| if (mlir::isa<fir::BoxType>(memref.getType())) |
| return CharacterExprHelper(builder, loc).readLengthFromBox(memref); |
| fir::emitFatalError(loc, "memref has wrong type"); |
| } |
| if (typeParams.empty()) { |
| fir::emitFatalError(loc, "array_load must have typeparams"); |
| } |
| if (fir::isa_char(seqTy.getEleTy())) { |
| assert(typeParams.size() == 1 && "too many typeparams"); |
| return typeParams.front(); |
| } |
| TODO(loc, "LEN of character must be computed at runtime"); |
| } |
| |
| mlir::Value fir::factory::createZeroValue(fir::FirOpBuilder &builder, |
| mlir::Location loc, mlir::Type type) { |
| mlir::Type i1 = builder.getIntegerType(1); |
| if (mlir::isa<fir::LogicalType>(type) || type == i1) |
| return builder.createConvert(loc, type, builder.createBool(loc, false)); |
| if (fir::isa_integer(type)) |
| return builder.createIntegerConstant(loc, type, 0); |
| if (fir::isa_real(type)) |
| return builder.createRealZeroConstant(loc, type); |
| if (fir::isa_complex(type)) { |
| fir::factory::Complex complexHelper(builder, loc); |
| mlir::Type partType = complexHelper.getComplexPartType(type); |
| mlir::Value zeroPart = builder.createRealZeroConstant(loc, partType); |
| return complexHelper.createComplex(type, zeroPart, zeroPart); |
| } |
| fir::emitFatalError(loc, "internal: trying to generate zero value of non " |
| "numeric or logical type"); |
| } |
| |
| std::optional<std::int64_t> |
| fir::factory::getExtentFromTriplet(mlir::Value lb, mlir::Value ub, |
| mlir::Value stride) { |
| std::function<std::optional<std::int64_t>(mlir::Value)> getConstantValue = |
| [&](mlir::Value value) -> std::optional<std::int64_t> { |
| if (auto valInt = fir::getIntIfConstant(value)) |
| return *valInt; |
| auto *definingOp = value.getDefiningOp(); |
| if (mlir::isa_and_nonnull<fir::ConvertOp>(definingOp)) { |
| auto valOp = mlir::dyn_cast<fir::ConvertOp>(definingOp); |
| return getConstantValue(valOp.getValue()); |
| } |
| return {}; |
| }; |
| if (auto lbInt = getConstantValue(lb)) { |
| if (auto ubInt = getConstantValue(ub)) { |
| if (auto strideInt = getConstantValue(stride)) { |
| if (*strideInt != 0) { |
| std::int64_t extent = 1 + (*ubInt - *lbInt) / *strideInt; |
| if (extent > 0) |
| return extent; |
| } |
| } |
| } |
| } |
| return {}; |
| } |
| |
| mlir::Value fir::factory::genMaxWithZero(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| mlir::Value value) { |
| mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0); |
| if (mlir::Operation *definingOp = value.getDefiningOp()) |
| if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp)) |
| if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(cst.getValue())) |
| return intAttr.getInt() > 0 ? value : zero; |
| mlir::Value valueIsGreater = builder.create<mlir::arith::CmpIOp>( |
| loc, mlir::arith::CmpIPredicate::sgt, value, zero); |
| return builder.create<mlir::arith::SelectOp>(loc, valueIsGreater, value, |
| zero); |
| } |
| |
| mlir::Value fir::factory::genCPtrOrCFunptrAddr(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| mlir::Value cPtr, |
| mlir::Type ty) { |
| assert(mlir::isa<fir::RecordType>(ty)); |
| auto recTy = mlir::dyn_cast<fir::RecordType>(ty); |
| assert(recTy.getTypeList().size() == 1); |
| auto fieldName = recTy.getTypeList()[0].first; |
| mlir::Type fieldTy = recTy.getTypeList()[0].second; |
| auto fieldIndexType = fir::FieldType::get(ty.getContext()); |
| mlir::Value field = |
| builder.create<fir::FieldIndexOp>(loc, fieldIndexType, fieldName, recTy, |
| /*typeParams=*/mlir::ValueRange{}); |
| return builder.create<fir::CoordinateOp>(loc, builder.getRefType(fieldTy), |
| cPtr, field); |
| } |
| |
| fir::BoxValue fir::factory::createBoxValue(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| const fir::ExtendedValue &exv) { |
| if (auto *boxValue = exv.getBoxOf<fir::BoxValue>()) |
| return *boxValue; |
| mlir::Value box = builder.createBox(loc, exv); |
| llvm::SmallVector<mlir::Value> lbounds; |
| llvm::SmallVector<mlir::Value> explicitTypeParams; |
| exv.match( |
| [&](const fir::ArrayBoxValue &box) { |
| lbounds.append(box.getLBounds().begin(), box.getLBounds().end()); |
| }, |
| [&](const fir::CharArrayBoxValue &box) { |
| lbounds.append(box.getLBounds().begin(), box.getLBounds().end()); |
| explicitTypeParams.emplace_back(box.getLen()); |
| }, |
| [&](const fir::CharBoxValue &box) { |
| explicitTypeParams.emplace_back(box.getLen()); |
| }, |
| [&](const fir::MutableBoxValue &x) { |
| if (x.rank() > 0) { |
| // The resulting box lbounds must be coming from the mutable box. |
| fir::ExtendedValue boxVal = |
| fir::factory::genMutableBoxRead(builder, loc, x); |
| // Make sure we do not recurse infinitely. |
| if (boxVal.getBoxOf<fir::MutableBoxValue>()) |
| fir::emitFatalError(loc, "mutable box read cannot be mutable box"); |
| fir::BoxValue box = |
| fir::factory::createBoxValue(builder, loc, boxVal); |
| lbounds.append(box.getLBounds().begin(), box.getLBounds().end()); |
| } |
| explicitTypeParams.append(x.nonDeferredLenParams().begin(), |
| x.nonDeferredLenParams().end()); |
| }, |
| [](const auto &) {}); |
| return fir::BoxValue(box, lbounds, explicitTypeParams); |
| } |
| |
| mlir::Value fir::factory::genCPtrOrCFunptrValue(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| mlir::Value cPtr) { |
| mlir::Type cPtrTy = fir::unwrapRefType(cPtr.getType()); |
| mlir::Value cPtrAddr = |
| fir::factory::genCPtrOrCFunptrAddr(builder, loc, cPtr, cPtrTy); |
| return builder.create<fir::LoadOp>(loc, cPtrAddr); |
| } |
| |
| mlir::Value fir::factory::createNullBoxProc(fir::FirOpBuilder &builder, |
| mlir::Location loc, |
| mlir::Type boxType) { |
| auto boxTy{mlir::dyn_cast<fir::BoxProcType>(boxType)}; |
| if (!boxTy) |
| fir::emitFatalError(loc, "Procedure pointer must be of BoxProcType"); |
| auto boxEleTy{fir::unwrapRefType(boxTy.getEleTy())}; |
| mlir::Value initVal{builder.create<fir::ZeroOp>(loc, boxEleTy)}; |
| return builder.create<fir::EmboxProcOp>(loc, boxTy, initVal); |
| } |
| |
| void fir::factory::setInternalLinkage(mlir::func::FuncOp func) { |
| auto internalLinkage = mlir::LLVM::linkage::Linkage::Internal; |
| auto linkage = |
| mlir::LLVM::LinkageAttr::get(func->getContext(), internalLinkage); |
| func->setAttr("llvm.linkage", linkage); |
| } |