//===-- 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/Allocatable.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/CUF/CUFOps.h" #include "flang/Optimizer/Dialect/FIRAttr.h" #include "flang/Optimizer/Dialect/FIRDialect.h" #include "flang/Optimizer/Dialect/FIROpsSupport.h" #include "flang/Optimizer/Dialect/FIRType.h" #include "flang/Optimizer/Support/DataLayout.h" #include "flang/Optimizer/Support/FatalError.h" #include "flang/Optimizer/Support/InternalNames.h" #include "flang/Optimizer/Support/Utils.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 static llvm::cl::opt 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::createRuntimeFunction(mlir::Location loc, llvm::StringRef name, mlir::FunctionType ty, bool isIO) { mlir::func::FuncOp func = createFunction(loc, name, ty); func->setAttr(fir::FIROpsDialect::getFirRuntimeAttrName(), getUnitAttr()); if (isIO) func->setAttr("fir.io", getUnitAttr()); return func; } mlir::func::FuncOp fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp, const mlir::SymbolTable *symbolTable, llvm::StringRef name) { if (symbolTable) if (auto func = symbolTable->lookup(name)) { #ifdef EXPENSIVE_CHECKS assert(func == modOp.lookupSymbol(name) && "symbolTable and module out of sync"); #endif return func; } return modOp.lookupSymbol(name); } mlir::func::FuncOp fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp, const mlir::SymbolTable *symbolTable, mlir::SymbolRefAttr symbol) { if (symbolTable) if (auto func = symbolTable->lookup( symbol.getLeafReference())) { #ifdef EXPENSIVE_CHECKS assert(func == modOp.lookupSymbol(symbol) && "symbolTable and module out of sync"); #endif return func; } return modOp.lookupSymbol(symbol); } fir::GlobalOp fir::FirOpBuilder::getNamedGlobal(mlir::ModuleOp modOp, const mlir::SymbolTable *symbolTable, llvm::StringRef name) { if (symbolTable) if (auto global = symbolTable->lookup(name)) { #ifdef EXPENSIVE_CHECKS assert(global == modOp.lookupSymbol(name) && "symbolTable and module out of sync"); #endif return global; } return modOp.lookupSymbol(name); } mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy, bool isVolatile) { assert(!mlir::isa(eleTy) && "cannot be a reference type"); return fir::ReferenceType::get(eleTy, isVolatile); } 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::Float16Type::get(getContext()); case llvm::Type::TypeID::BFloatTyID: return mlir::BFloat16Type::get(getContext()); case llvm::Type::TypeID::FloatTyID: return mlir::Float32Type::get(getContext()); case llvm::Type::TypeID::DoubleTyID: return mlir::Float64Type::get(getContext()); case llvm::Type::TypeID::X86_FP80TyID: return mlir::Float80Type::get(getContext()); case llvm::Type::TypeID::FP128TyID: return mlir::Float128Type::get(getContext()); default: fir::emitFatalError(mlir::UnknownLoc::get(getContext()), "unsupported type !fir.real"); } } mlir::Value fir::FirOpBuilder::createNullConstant(mlir::Location loc, mlir::Type ptrType) { auto ty = ptrType ? ptrType : getRefType(getNoneType()); return create(loc, ty); } mlir::Value fir::FirOpBuilder::createIntegerConstant(mlir::Location loc, mlir::Type ty, std::int64_t cst) { assert((cst >= 0 || mlir::isa(ty) || mlir::cast(ty).getWidth() <= 64) && "must use APint"); return create(loc, ty, getIntegerAttr(ty, cst)); } mlir::Value fir::FirOpBuilder::createAllOnesInteger(mlir::Location loc, mlir::Type ty) { if (mlir::isa(ty)) return createIntegerConstant(loc, ty, -1); llvm::APInt allOnes = llvm::APInt::getAllOnes(mlir::cast(ty).getWidth()); return create(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 (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(fltTy)) { auto attr = getFloatAttr(fltTy, value); return create(loc, fltTy, attr); } llvm_unreachable("should use builtin floating-point type"); } llvm::SmallVector fir::factory::elideExtentsAlreadyInType(mlir::Type type, mlir::ValueRange shape) { auto arrTy = mlir::dyn_cast(type); if (shape.empty() || !arrTy) return {}; // elide the constant dimensions before construction assert(shape.size() == arrTy.getDimension()); llvm::SmallVector 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 fir::factory::elideLengthsAlreadyInType(mlir::Type type, mlir::ValueRange lenParams) { if (lenParams.empty()) return {}; if (auto arrTy = mlir::dyn_cast(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 shape, llvm::ArrayRef lenParams, bool asTarget) { // Convert the shape extents to `index`, as needed. llvm::SmallVector indices; llvm::SmallVector elidedShape = fir::factory::elideExtentsAlreadyInType(ty, shape); llvm::SmallVector 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 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(loc, ty, pinned, elidedLenParams, indices, attrs); return create(loc, ty, uniqName, pinned, elidedLenParams, indices, attrs); } return create(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 shape, llvm::ArrayRef 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 accComputeRegionIface = getRegion().getParentOfType()) { return accComputeRegionIface.getAllocaBlock(); } if (auto ompOutlineableIface = getRegion() .getParentOfType()) { return ompOutlineableIface.getAllocaBlock(); } if (auto recipeIface = getRegion().getParentOfType()) { return recipeIface.getAllocaBlock(getRegion()); } if (auto cufKernelOp = getRegion().getParentOfType()) return &cufKernelOp.getRegion().front(); if (auto doConcurentOp = getRegion().getParentOfType()) return doConcurentOp.getBody(); if (auto firLocalOp = getRegion().getParentOfType()) return &getRegion().front(); if (auto firLocalOp = getRegion().getParentOfType()) return &getRegion().front(); return getEntryBlock(); } static mlir::ArrayAttr makeI64ArrayAttr(llvm::ArrayRef values, mlir::MLIRContext *context) { llvm::SmallVector attrs; attrs.reserve(values.size()); for (auto &v : values) attrs.push_back(mlir::IntegerAttr::get(mlir::IntegerType::get(context, 64), mlir::APInt(64, v))); return mlir::ArrayAttr::get(context, attrs); } mlir::ArrayAttr fir::FirOpBuilder::create2DI64ArrayAttr( llvm::SmallVectorImpl> &intData) { llvm::SmallVector arrayAttr; arrayAttr.reserve(intData.size()); mlir::MLIRContext *context = getContext(); for (auto &v : intData) arrayAttr.push_back(makeI64ArrayAttr(v, context)); return mlir::ArrayAttr::get(context, arrayAttr); } mlir::Value fir::FirOpBuilder::createTemporaryAlloc( mlir::Location loc, mlir::Type type, llvm::StringRef name, mlir::ValueRange lenParams, mlir::ValueRange shape, llvm::ArrayRef attrs, std::optional cudaAttr) { assert(!mlir::isa(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(); if (cudaAttr) { cuf::DataAttributeAttr attr = cuf::getDataAttribute(getContext(), cudaAttr); return create(loc, type, /*unique_name=*/llvm::StringRef{}, name, attr, lenParams, shape, attrs); } else { return create(loc, type, /*unique_name=*/llvm::StringRef{}, name, pinned, lenParams, shape, attrs); } } /// 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 attrs, std::optional cudaAttr) { llvm::SmallVector dynamicShape = fir::factory::elideExtentsAlreadyInType(type, shape); llvm::SmallVector 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, cudaAttr); 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 attrs) { llvm::SmallVector dynamicShape = fir::factory::elideExtentsAlreadyInType(type, shape); llvm::SmallVector dynamicLength = fir::factory::elideLengthsAlreadyInType(type, lenParams); assert(!mlir::isa(type) && "cannot be a reference"); return create(loc, type, /*unique_name=*/llvm::StringRef{}, name, dynamicLength, dynamicShape, attrs); } std::pair fir::FirOpBuilder::createAndDeclareTemp( mlir::Location loc, mlir::Type baseType, mlir::Value shape, llvm::ArrayRef extents, llvm::ArrayRef typeParams, const std::function &genDeclare, mlir::Value polymorphicMold, bool useStack, llvm::StringRef tmpName) { if (polymorphicMold) { // Create *allocated* polymorphic temporary using the dynamic type // of the mold and the provided shape/extents. auto boxType = fir::ClassType::get(fir::HeapType::get(baseType)); mlir::Value boxAddress = fir::factory::getAndEstablishBoxStorage( *this, loc, boxType, shape, typeParams, polymorphicMold); fir::runtime::genAllocatableAllocate(*this, loc, boxAddress); mlir::Value box = create(loc, boxAddress); mlir::Value base = genDeclare(*this, loc, box, tmpName, /*shape=*/mlir::Value{}, typeParams, fir::FortranVariableFlagsAttr{}); return {base, /*isHeapAllocation=*/true}; } mlir::Value allocmem; if (useStack) allocmem = createTemporary(loc, baseType, tmpName, extents, typeParams); else allocmem = createHeapTemporary(loc, baseType, tmpName, extents, typeParams); mlir::Value base = genDeclare(*this, loc, allocmem, tmpName, shape, typeParams, fir::FortranVariableFlagsAttr{}); return {base, !useStack}; } mlir::Value fir::FirOpBuilder::genTempDeclareOp( fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value memref, llvm::StringRef name, mlir::Value shape, llvm::ArrayRef typeParams, fir::FortranVariableFlagsAttr fortranAttrs) { auto nameAttr = mlir::StringAttr::get(builder.getContext(), name); return fir::DeclareOp::create(builder, loc, memref.getType(), memref, shape, typeParams, /*dummy_scope=*/nullptr, nameAttr, fortranAttrs, cuf::DataAttributeAttr{}); } mlir::Value fir::FirOpBuilder::genStackSave(mlir::Location loc) { mlir::Type voidPtr = mlir::LLVM::LLVMPointerType::get( getContext(), fir::factory::getAllocaAddressSpace(&getDataLayout())); return create(loc, voidPtr); } void fir::FirOpBuilder::genStackRestore(mlir::Location loc, mlir::Value stackPointer) { create(loc, stackPointer); } /// 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, cuf::DataAttributeAttr dataAttr) { if (auto global = getNamedGlobal(name)) return global; auto module = getModule(); auto insertPt = saveInsertionPoint(); setInsertionPoint(module.getBody(), module.getBody()->end()); llvm::SmallVector attrs; if (dataAttr) { auto globalOpName = mlir::OperationName(fir::GlobalOp::getOperationName(), module.getContext()); attrs.push_back(mlir::NamedAttribute( fir::GlobalOp::getDataAttrAttrName(globalOpName), dataAttr)); } auto glob = create(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 bodyBuilder, mlir::StringAttr linkage, cuf::DataAttributeAttr dataAttr) { if (auto global = getNamedGlobal(name)) return global; auto module = getModule(); auto insertPt = saveInsertionPoint(); setInsertionPoint(module.getBody(), module.getBody()->end()); auto glob = create(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; } std::pair fir::FirOpBuilder::createTypeInfoOp(mlir::Location loc, fir::RecordType recordType, fir::RecordType parentType) { mlir::ModuleOp module = getModule(); if (fir::TypeInfoOp typeInfo = fir::lookupTypeInfoOp(recordType.getName(), module, symbolTable)) return {typeInfo, InsertPoint{}}; InsertPoint insertPoint = saveInsertionPoint(); setInsertionPoint(module.getBody(), module.getBody()->end()); auto typeInfo = create(loc, recordType, parentType); if (symbolTable) symbolTable->insert(typeInfo); return {typeInfo, insertPoint}; } 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); auto imag = createRealZeroConstant(loc, eleTy); 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(fromTy)) { // Extract the address of the character string and pass it fir::factory::CharacterExprHelper charHelper{*this, loc}; std::pair unboxchar = charHelper.createUnboxChar(val); return createConvert(loc, toTy, unboxchar.first); } if (auto boxType = mlir::dyn_cast(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); // Do not use fir.undef since llvm optimizer is too harsh when it // sees such values (may just delete code). mlir::Value unknownLen = createIntegerConstant(loc, getIndexType(), 0); 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(loc, toTy, val); } if (fir::isa_ref_type(fromTy) && mlir::isa(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(toTy).getEleTy(); mlir::Value proc = createConvert(loc, procTy, val); return create(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(toTy))) && !(fir::isUnlimitedPolymorphicType(fromTy) && fir::isAssumedType(toTy))) return create(loc, toTy, val, mlir::Value{}, /*slice=*/mlir::Value{}); return createConvert(loc, toTy, val); } mlir::Value fir::FirOpBuilder::createVolatileCast(mlir::Location loc, bool isVolatile, mlir::Value val) { mlir::Type volatileAdjustedType = fir::updateTypeWithVolatility(val.getType(), isVolatile); if (volatileAdjustedType == val.getType()) return val; return create(loc, volatileAdjustedType, val); } mlir::Value fir::FirOpBuilder::createConvertWithVolatileCast(mlir::Location loc, mlir::Type toTy, mlir::Value val) { val = createVolatileCast(loc, fir::isa_volatile_type(toTy), val); return createConvert(loc, toTy, val); } mlir::Value fir::factory::createConvert(mlir::OpBuilder &builder, mlir::Location loc, mlir::Type toTy, mlir::Value val) { if (val.getType() != toTy) { assert((!fir::isa_derived(toTy) || mlir::cast(val.getType()).getTypeList() == mlir::cast(toTy).getTypeList()) && "incompatible record types"); return fir::ConvertOp::create(builder, loc, toTy, val); } return val; } mlir::Value fir::FirOpBuilder::createConvert(mlir::Location loc, mlir::Type toTy, mlir::Value val) { return fir::factory::createConvert(*this, loc, toTy, val); } void fir::FirOpBuilder::createStoreWithConvert(mlir::Location loc, mlir::Value val, mlir::Value addr) { mlir::Type unwrapedRefType = fir::unwrapRefType(addr.getType()); val = createVolatileCast(loc, fir::isa_volatile_type(unwrapedRefType), val); mlir::Value cast = createConvert(loc, unwrapedRefType, val); create(loc, cast, addr); } mlir::Value fir::FirOpBuilder::loadIfRef(mlir::Location loc, mlir::Value val) { if (fir::isa_ref_type(val.getType())) return create(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 attrs{dataAttr, sizeAttr}; return create(loc, llvm::ArrayRef{type}, mlir::ValueRange{}, attrs); } mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, llvm::ArrayRef exts) { return create(loc, exts); } mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, llvm::ArrayRef shift, llvm::ArrayRef exts) { auto shapeType = fir::ShapeShiftType::get(getContext(), exts.size()); llvm::SmallVector 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(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 shift) { auto shiftType = fir::ShiftType::get(getContext(), shift.size()); return create(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(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 lbounds, llvm::ArrayRef extents) -> mlir::Value { llvm::SmallVector 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(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(loc, lb, one); auto ub = create(loc, ext, shift); trips.push_back(lb); trips.push_back(ub); trips.push_back(one); } return create(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(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(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"); } const bool isVolatile = fir::isa_volatile_type(itemAddr.getType()); mlir::Type boxTy; mlir::Value tdesc; // Avoid to wrap a box/class with box/class. if (mlir::isa(elementType)) { boxTy = elementType; } else { boxTy = fir::BoxType::get(elementType, isVolatile); if (isPolymorphic) { elementType = fir::updateTypeForUnlimitedPolymorphic(elementType); if (isAssumedType) boxTy = fir::BoxType::get(elementType, isVolatile); else boxTy = fir::ClassType::get(elementType, isVolatile); } } return exv.match( [&](const fir::ArrayBoxValue &box) -> mlir::Value { mlir::Value empty; mlir::ValueRange emptyRange; mlir::Value s = createShape(loc, exv); return create(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(loc, boxTy, itemAddr, s); mlir::Value emptySlice; llvm::SmallVector lenParams{box.getLen()}; return create(loc, boxTy, itemAddr, s, emptySlice, lenParams); }, [&](const fir::CharBoxValue &box) -> mlir::Value { if (fir::factory::CharacterExprHelper::hasConstantLengthInType(exv)) return create(loc, boxTy, itemAddr); mlir::Value emptyShape, emptySlice; llvm::SmallVector lenParams{box.getLen()}; return create(loc, boxTy, itemAddr, emptyShape, emptySlice, lenParams); }, [&](const fir::MutableBoxValue &x) -> mlir::Value { return create( loc, fir::factory::getMutableIRBox(*this, loc, x)); }, [&](const fir::PolymorphicValue &p) -> mlir::Value { mlir::Value empty; mlir::ValueRange emptyRange; return create(loc, boxTy, itemAddr, empty, empty, emptyRange, isPolymorphic ? p.getSourceBox() : tdesc); }, [&](const auto &) -> mlir::Value { mlir::Value empty; mlir::ValueRange emptyRange; return create(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 lengths, mlir::Value tdesc) { mlir::Type valueOrSequenceType = fir::unwrapPassByRefType(boxType); return create( 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 mlir::arith::CmpIOp::create(builder, 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(loc, ub, lb); auto add = create(loc, diff, step); auto div = create(loc, add, step); auto cmp = create(loc, mlir::arith::CmpIPredicate::sgt, div, zero); return create(loc, cmp, div, zero); } mlir::Value fir::FirOpBuilder::genAbsentOp(mlir::Location loc, mlir::Type argTy) { if (!fir::isCharacterProcedureTuple(argTy)) return create(loc, argTy); auto boxProc = create(loc, mlir::cast(argTy).getType(0)); mlir::Value charLen = create(loc, getCharacterLengthType()); return fir::factory::createCharacterProcedureTuple(*this, loc, argTy, boxProc, charLen); } void fir::FirOpBuilder::setCommonAttributes(mlir::Operation *op) const { auto fmi = mlir::dyn_cast(*op); if (fmi) { // 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)); } auto iofi = mlir::dyn_cast(*op); if (iofi) { llvm::StringRef arithIOFAttrName = iofi.getIntegerOverflowAttrName(); if (integerOverflowFlags != mlir::arith::IntegerOverflowFlags::none) op->setAttr(arithIOFAttrName, mlir::arith::IntegerOverflowFlagsAttr::get( op->getContext(), integerOverflowFlags)); } } 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); } // Construction of an mlir::DataLayout is expensive so only do it on demand and // memoise it in the builder instance mlir::DataLayout &fir::FirOpBuilder::getDataLayout() { if (dataLayout) return *dataLayout; dataLayout = std::make_unique(getModule()); return *dataLayout; } //===--------------------------------------------------------------------===// // 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(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 fir::factory::readExtents(fir::FirOpBuilder &builder, mlir::Location loc, const fir::BoxValue &box) { llvm::SmallVector 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 = fir::BoxDimsOp::create(builder, loc, idxTy, idxTy, idxTy, box.getAddr(), dimVal); result.emplace_back(dimInfo.getResult(1)); } return result; } llvm::SmallVector fir::factory::getExtents(mlir::Location loc, fir::FirOpBuilder &builder, const fir::ExtendedValue &box) { return box.match( [&](const fir::ArrayBoxValue &x) -> llvm::SmallVector { return {x.getExtents().begin(), x.getExtents().end()}; }, [&](const fir::CharArrayBoxValue &x) -> llvm::SmallVector { return {x.getExtents().begin(), x.getExtents().end()}; }, [&](const fir::BoxValue &x) -> llvm::SmallVector { return fir::factory::readExtents(builder, loc, x); }, [&](const fir::MutableBoxValue &x) -> llvm::SmallVector { auto load = fir::factory::genMutableBoxRead(builder, loc, x); return fir::factory::getExtents(loc, builder, load); }, [&](const auto &) -> llvm::SmallVector { 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 = fir::BoxAddrOp::create(builder, 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 fir::factory::getNonDefaultLowerBounds(fir::FirOpBuilder &builder, mlir::Location loc, const fir::ExtendedValue &exv) { return exv.match( [&](const fir::ArrayBoxValue &array) -> llvm::SmallVector { return {array.getLBounds().begin(), array.getLBounds().end()}; }, [&](const fir::CharArrayBoxValue &array) -> llvm::SmallVector { return {array.getLBounds().begin(), array.getLBounds().end()}; }, [&](const fir::BoxValue &box) -> llvm::SmallVector { return {box.getLBounds().begin(), box.getLBounds().end()}; }, [&](const fir::MutableBoxValue &box) -> llvm::SmallVector { auto load = fir::factory::genMutableBoxRead(builder, loc, box); return fir::factory::getNonDefaultLowerBounds(builder, loc, load); }, [&](const auto &) -> llvm::SmallVector { return {}; }); } llvm::SmallVector fir::factory::getNonDeferredLenParams(const fir::ExtendedValue &exv) { return exv.match( [&](const fir::CharArrayBoxValue &character) -> llvm::SmallVector { return {character.getLen()}; }, [&](const fir::CharBoxValue &character) -> llvm::SmallVector { return {character.getLen()}; }, [&](const fir::MutableBoxValue &box) -> llvm::SmallVector { return {box.nonDeferredLenParams().begin(), box.nonDeferredLenParams().end()}; }, [&](const fir::BoxValue &box) -> llvm::SmallVector { return {box.getExplicitParameters().begin(), box.getExplicitParameters().end()}; }, [&](const auto &) -> llvm::SmallVector { return {}; }); } // If valTy is a box type, then we need to extract the type parameters from // the box value. static llvm::SmallVector getFromBox(mlir::Location loc, fir::FirOpBuilder &builder, mlir::Type valTy, mlir::Value boxVal) { if (auto boxTy = mlir::dyn_cast(valTy)) { auto eleTy = fir::unwrapAllRefAndSeqType(boxTy.getEleTy()); if (auto recTy = mlir::dyn_cast(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(eleTy)) { if (charTy.hasDynamicLen()) { auto idxTy = builder.getIndexType(); auto eleSz = fir::BoxEleSizeOp::create(builder, loc, idxTy, boxVal); auto kindBytes = builder.getKindMap().getCharacterBitsize(charTy.getFKind()) / 8; mlir::Value charSz = builder.createIntegerConstant(loc, idxTy, kindBytes); mlir::Value len = mlir::arith::DivSIOp::create(builder, 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 fir::factory::getTypeParams(mlir::Location loc, fir::FirOpBuilder &builder, const fir::ExtendedValue &exv) { auto handleBoxed = [&](const auto &box) -> llvm::SmallVector { 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 fir::factory::getTypeParams(mlir::Location loc, fir::FirOpBuilder &builder, fir::ArrayLoadOp load) { mlir::Type memTy = load.getMemref().getType(); if (auto boxTy = mlir::dyn_cast(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(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(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); fir::HasValueOp::create(builder, loc, stringLitOp); }, builder.createLinkOnceLinkage()); auto addr = fir::AddrOfOp::create(builder, loc, global.resultType(), global.getSymbol()); auto len = builder.createIntegerConstant( loc, builder.getCharacterLengthType(), str.size()); return fir::CharBoxValue{addr, len}; } llvm::SmallVector fir::factory::createExtents(fir::FirOpBuilder &builder, mlir::Location loc, fir::SequenceType seqTy) { llvm::SmallVector extents; auto idxTy = builder.getIndexType(); for (auto ext : seqTy.getShape()) extents.emplace_back( ext == fir::SequenceType::getUnknownExtent() ? fir::UndefOp::create(builder, 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(fieldTy)) { llvm::SmallVector nonDeferredTypeParams; auto eleTy = fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(fieldTy)); if (auto charTy = mlir::dyn_cast(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(eleTy)) if (recTy.getNumLenParams() > 0) TODO(loc, "allocatable and pointer components non deferred length " "parameters"); return fir::MutableBoxValue(component, nonDeferredTypeParams, /*mutableProperties=*/{}); } llvm::SmallVector extents; if (auto seqTy = mlir::dyn_cast(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(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(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(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(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(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 = fir::LoadOp::create(builder, loc, rhsVal); mlir::Value lhsAddr = fir::getBase(lhs); rhsVal = builder.createConvert(loc, fir::unwrapRefType(lhsAddr.getType()), rhsVal); fir::StoreOp::create(builder, 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(lbaseType); assert(lhsType && "lhs must be a scalar record type"); auto rbaseType = fir::unwrapPassByRefType(fir::getBase(rhs).getType()); auto rhsType = mlir::dyn_cast(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 = fir::FieldIndexOp::create(builder, loc, fieldIndexType, rFieldName, rhsType, fir::getTypeParams(rhs)); auto rFieldRefType = builder.getRefType(rFieldTy); mlir::Value fromCoor = fir::CoordinateOp::create( builder, loc, rFieldRefType, fir::getBase(rhs), rField); mlir::Value field = fir::FieldIndexOp::create(builder, loc, fieldIndexType, lFieldName, lhsType, fir::getTypeParams(lhs)); auto fieldRefType = builder.getRefType(lFieldTy); mlir::Value toCoor = fir::CoordinateOp::create(builder, loc, fieldRefType, fir::getBase(lhs), field); std::optional outerLoop; if (auto sequenceType = mlir::dyn_cast(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 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 = fir::DoLoopOp::create(builder, 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 = fir::CoordinateOp::create(builder, loc, elementRefType, toCoor, indices); fromCoor = fir::CoordinateOp::create(builder, loc, elementRefType, fromCoor, indices); } if (auto fieldEleTy = fir::unwrapSequenceType(lFieldTy); mlir::isa(fieldEleTy)) { assert(mlir::isa( mlir::cast(fieldEleTy).getEleTy()) && "allocatable members require deep copy"); auto fromPointerValue = fir::LoadOp::create(builder, loc, fromCoor); auto castTo = builder.createConvert(loc, fieldEleTy, fromPointerValue); fir::StoreOp::create(builder, 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) { // c_devptr type is a special case. It has a nested c_ptr field but we know it // can be copied directly. if (fir::isa_builtin_c_devptr_type(recordType)) return true; 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::unwrapSequenceType(fieldType)) && !fir::isa_builtin_c_devptr_type(fir::unwrapSequenceType(fieldType))) return false; // Allocatable components need deep copy. if (auto boxType = mlir::dyn_cast(fieldType)) if (mlir::isa(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( 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::getBase(lhs).getType()) || mlir::isa(fir::getBase(rhs).getType()); auto recTy = mlir::dyn_cast(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> 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()); fir::StoreOp::create(builder, 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 path, llvm::ArrayRef substring) { llvm::SmallVector typeParams(arrLoad.getTypeparams()); return genLenOfCharacter(builder, loc, mlir::cast(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 typeParams, llvm::ArrayRef path, llvm::ArrayRef substring) { auto idxTy = builder.getIndexType(); auto zero = builder.createIntegerConstant(loc, idxTy, 0); auto saturatedDiff = [&](mlir::Value lower, mlir::Value upper) { auto diff = mlir::arith::SubIOp::create(builder, loc, upper, lower); auto one = builder.createIntegerConstant(loc, idxTy, 1); auto size = mlir::arith::AddIOp::create(builder, loc, diff, one); auto cmp = mlir::arith::CmpIOp::create( builder, loc, mlir::arith::CmpIPredicate::sgt, size, zero); return mlir::arith::SelectOp::create(builder, 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(eleTy)) { // Use LEN from the type. return builder.createIntegerConstant(loc, idxTy, charTy.getLen()); } // Do we need to support !fir.array>? fir::emitFatalError(loc, "application of path did not result in a !fir.char"); } if (fir::isa_box_type(memref.getType())) { if (mlir::isa(memref.getType())) return fir::BoxCharLenOp::create(builder, loc, idxTy, memref); if (mlir::isa(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(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 fir::factory::getExtentFromTriplet(mlir::Value lb, mlir::Value ub, mlir::Value stride) { std::function(mlir::Value)> getConstantValue = [&](mlir::Value value) -> std::optional { if (auto valInt = fir::getIntIfConstant(value)) return *valInt; auto *definingOp = value.getDefiningOp(); if (mlir::isa_and_nonnull(definingOp)) { auto valOp = mlir::dyn_cast(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) { if (mlir::Operation *definingOp = value.getDefiningOp()) if (auto cst = mlir::dyn_cast(definingOp)) if (auto intAttr = mlir::dyn_cast(cst.getValue())) return intAttr.getInt() > 0 ? value : zero; mlir::Value valueIsGreater = mlir::arith::CmpIOp::create( builder, loc, mlir::arith::CmpIPredicate::sgt, value, zero); return mlir::arith::SelectOp::create(builder, loc, valueIsGreater, value, zero); } mlir::Value fir::factory::genMaxWithZero(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value value) { mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0); return genMaxWithZero(builder, loc, value, zero); } mlir::Value fir::factory::computeExtent(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value lb, mlir::Value ub, mlir::Value zero, mlir::Value one) { mlir::Type type = lb.getType(); // Let the folder deal with the common `ub - + 1` case. auto diff = mlir::arith::SubIOp::create(builder, loc, type, ub, lb); auto rawExtent = mlir::arith::AddIOp::create(builder, loc, type, diff, one); return fir::factory::genMaxWithZero(builder, loc, rawExtent, zero); } mlir::Value fir::factory::computeExtent(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value lb, mlir::Value ub) { mlir::Type type = lb.getType(); mlir::Value one = builder.createIntegerConstant(loc, type, 1); mlir::Value zero = builder.createIntegerConstant(loc, type, 0); return computeExtent(builder, loc, lb, ub, zero, one); } static std::pair genCPtrOrCFunptrFieldIndex(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Type cptrTy) { auto recTy = mlir::cast(cptrTy); assert(recTy.getTypeList().size() == 1); auto addrFieldName = recTy.getTypeList()[0].first; mlir::Type addrFieldTy = recTy.getTypeList()[0].second; auto fieldIndexType = fir::FieldType::get(cptrTy.getContext()); mlir::Value addrFieldIndex = fir::FieldIndexOp::create( builder, loc, fieldIndexType, addrFieldName, recTy, /*typeParams=*/mlir::ValueRange{}); return {addrFieldIndex, addrFieldTy}; } mlir::Value fir::factory::genCPtrOrCFunptrAddr(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value cPtr, mlir::Type ty) { auto [addrFieldIndex, addrFieldTy] = genCPtrOrCFunptrFieldIndex(builder, loc, ty); return fir::CoordinateOp::create( builder, loc, builder.getRefType(addrFieldTy), cPtr, addrFieldIndex); } mlir::Value fir::factory::genCDevPtrAddr(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value cDevPtr, mlir::Type ty) { auto recTy = mlir::cast(ty); assert(recTy.getTypeList().size() == 1); auto cptrFieldName = recTy.getTypeList()[0].first; mlir::Type cptrFieldTy = recTy.getTypeList()[0].second; auto fieldIndexType = fir::FieldType::get(ty.getContext()); mlir::Value cptrFieldIndex = fir::FieldIndexOp::create( builder, loc, fieldIndexType, cptrFieldName, recTy, /*typeParams=*/mlir::ValueRange{}); auto cptrCoord = fir::CoordinateOp::create( builder, loc, builder.getRefType(cptrFieldTy), cDevPtr, cptrFieldIndex); auto [addrFieldIndex, addrFieldTy] = genCPtrOrCFunptrFieldIndex(builder, loc, cptrFieldTy); return fir::CoordinateOp::create( builder, loc, builder.getRefType(addrFieldTy), cptrCoord, addrFieldIndex); } mlir::Value fir::factory::genCPtrOrCFunptrValue(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value cPtr) { mlir::Type cPtrTy = fir::unwrapRefType(cPtr.getType()); if (fir::isa_builtin_cdevptr_type(cPtrTy)) { // Unwrap c_ptr from c_devptr. auto [addrFieldIndex, addrFieldTy] = genCPtrOrCFunptrFieldIndex(builder, loc, cPtrTy); mlir::Value cPtrCoor; if (fir::isa_ref_type(cPtr.getType())) { cPtrCoor = fir::CoordinateOp::create( builder, loc, builder.getRefType(addrFieldTy), cPtr, addrFieldIndex); } else { auto arrayAttr = builder.getArrayAttr( {builder.getIntegerAttr(builder.getIndexType(), 0)}); cPtrCoor = fir::ExtractValueOp::create(builder, loc, addrFieldTy, cPtr, arrayAttr); } return genCPtrOrCFunptrValue(builder, loc, cPtrCoor); } if (fir::isa_ref_type(cPtr.getType())) { mlir::Value cPtrAddr = fir::factory::genCPtrOrCFunptrAddr(builder, loc, cPtr, cPtrTy); return fir::LoadOp::create(builder, loc, cPtrAddr); } auto [addrFieldIndex, addrFieldTy] = genCPtrOrCFunptrFieldIndex(builder, loc, cPtrTy); auto arrayAttr = builder.getArrayAttr({builder.getIntegerAttr(builder.getIndexType(), 0)}); return fir::ExtractValueOp::create(builder, loc, addrFieldTy, cPtr, arrayAttr); } fir::BoxValue fir::factory::createBoxValue(fir::FirOpBuilder &builder, mlir::Location loc, const fir::ExtendedValue &exv) { if (auto *boxValue = exv.getBoxOf()) return *boxValue; mlir::Value box = builder.createBox(loc, exv); llvm::SmallVector lbounds; llvm::SmallVector 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::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::createNullBoxProc(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Type boxType) { auto boxTy{mlir::dyn_cast(boxType)}; if (!boxTy) fir::emitFatalError(loc, "Procedure pointer must be of BoxProcType"); auto boxEleTy{fir::unwrapRefType(boxTy.getEleTy())}; mlir::Value initVal{fir::ZeroOp::create(builder, loc, boxEleTy)}; return fir::EmboxProcOp::create(builder, 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); } uint64_t fir::factory::getAllocaAddressSpace(const mlir::DataLayout *dataLayout) { if (dataLayout) if (mlir::Attribute addrSpace = dataLayout->getAllocaMemorySpace()) return mlir::cast(addrSpace).getUInt(); return 0; } llvm::SmallVector fir::factory::deduceOptimalExtents(mlir::ValueRange extents1, mlir::ValueRange extents2) { llvm::SmallVector extents; extents.reserve(extents1.size()); for (auto [extent1, extent2] : llvm::zip(extents1, extents2)) { if (!fir::getIntIfConstant(extent1) && fir::getIntIfConstant(extent2)) extents.push_back(extent2); else extents.push_back(extent1); } return extents; } uint64_t fir::factory::getGlobalAddressSpace(mlir::DataLayout *dataLayout) { if (dataLayout) if (mlir::Attribute addrSpace = dataLayout->getGlobalMemorySpace()) return mlir::cast(addrSpace).getUInt(); return 0; } uint64_t fir::factory::getProgramAddressSpace(mlir::DataLayout *dataLayout) { if (dataLayout) if (mlir::Attribute addrSpace = dataLayout->getProgramMemorySpace()) return mlir::cast(addrSpace).getUInt(); return 0; } llvm::SmallVector fir::factory::updateRuntimeExtentsForEmptyArrays( fir::FirOpBuilder &builder, mlir::Location loc, mlir::ValueRange extents) { if (extents.size() <= 1) return extents; mlir::Type i1Type = builder.getI1Type(); mlir::Value isEmpty = createZeroValue(builder, loc, i1Type); llvm::SmallVector zeroes; for (mlir::Value extent : extents) { mlir::Type type = extent.getType(); mlir::Value zero = createZeroValue(builder, loc, type); zeroes.push_back(zero); mlir::Value isZero = mlir::arith::CmpIOp::create( builder, loc, mlir::arith::CmpIPredicate::eq, extent, zero); isEmpty = mlir::arith::OrIOp::create(builder, loc, isEmpty, isZero); } llvm::SmallVector newExtents; for (auto [zero, extent] : llvm::zip_equal(zeroes, extents)) { newExtents.push_back( mlir::arith::SelectOp::create(builder, loc, isEmpty, zero, extent)); } return newExtents; } void fir::factory::genDimInfoFromBox( fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value box, llvm::SmallVectorImpl *lbounds, llvm::SmallVectorImpl *extents, llvm::SmallVectorImpl *strides) { auto boxType = mlir::dyn_cast(box.getType()); assert(boxType && "must be a box"); if (!lbounds && !extents && !strides) return; unsigned rank = fir::getBoxRank(boxType); assert(rank != 0 && "must be an array of known rank"); mlir::Type idxTy = builder.getIndexType(); for (unsigned i = 0; i < rank; ++i) { mlir::Value dim = builder.createIntegerConstant(loc, idxTy, i); auto dimInfo = fir::BoxDimsOp::create(builder, loc, idxTy, idxTy, idxTy, box, dim); if (lbounds) lbounds->push_back(dimInfo.getLowerBound()); if (extents) extents->push_back(dimInfo.getExtent()); if (strides) strides->push_back(dimInfo.getByteStride()); } } mlir::Value fir::factory::genLifetimeStart(mlir::OpBuilder &builder, mlir::Location loc, fir::AllocaOp alloc, int64_t size, const mlir::DataLayout *dl) { mlir::Type ptrTy = mlir::LLVM::LLVMPointerType::get( alloc.getContext(), getAllocaAddressSpace(dl)); mlir::Value cast = fir::ConvertOp::create(builder, loc, ptrTy, alloc.getResult()); mlir::LLVM::LifetimeStartOp::create(builder, loc, size, cast); return cast; } void fir::factory::genLifetimeEnd(mlir::OpBuilder &builder, mlir::Location loc, mlir::Value cast, int64_t size) { mlir::LLVM::LifetimeEndOp::create(builder, loc, size, cast); }