//===-- HLFIRTools.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 // //===----------------------------------------------------------------------===// // // Tools to manipulate HLFIR variable and expressions // //===----------------------------------------------------------------------===// #include "flang/Optimizer/Builder/HLFIRTools.h" #include "flang/Optimizer/Builder/FIRBuilder.h" #include "flang/Optimizer/Builder/MutableBox.h" #include "flang/Optimizer/Builder/Todo.h" #include "flang/Optimizer/HLFIR/HLFIROps.h" // Return explicit extents. If the base is a fir.box, this won't read it to // return the extents and will instead return an empty vector. static llvm::SmallVector getExplicitExtents(fir::FortranVariableOpInterface var) { llvm::SmallVector result; if (mlir::Value shape = var.getShape()) { auto *shapeOp = shape.getDefiningOp(); if (auto s = mlir::dyn_cast_or_null(shapeOp)) { auto e = s.getExtents(); result.append(e.begin(), e.end()); } else if (auto s = mlir::dyn_cast_or_null(shapeOp)) { auto e = s.getExtents(); result.append(e.begin(), e.end()); } else if (mlir::dyn_cast_or_null(shapeOp)) { return {}; } else { TODO(var->getLoc(), "read fir.shape to get extents"); } } return result; } // Return explicit lower bounds. For pointers and allocatables, this will not // read the lower bounds and instead return an empty vector. static llvm::SmallVector getExplicitLbounds(fir::FortranVariableOpInterface var) { llvm::SmallVector result; if (mlir::Value shape = var.getShape()) { auto *shapeOp = shape.getDefiningOp(); if (auto s = mlir::dyn_cast_or_null(shapeOp)) { return {}; } else if (auto s = mlir::dyn_cast_or_null(shapeOp)) { auto e = s.getOrigins(); result.append(e.begin(), e.end()); } else if (auto s = mlir::dyn_cast_or_null(shapeOp)) { auto e = s.getOrigins(); result.append(e.begin(), e.end()); } else { TODO(var->getLoc(), "read fir.shape to get lower bounds"); } } return result; } static llvm::SmallVector getExplicitTypeParams(fir::FortranVariableOpInterface var) { llvm::SmallVector res; mlir::OperandRange range = var.getExplicitTypeParams(); res.append(range.begin(), range.end()); return res; } std::pair> hlfir::translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity entity) { if (auto variable = entity.getIfVariableInterface()) return {hlfir::translateToExtendedValue(loc, builder, variable), {}}; if (entity.isVariable()) { if (entity.isScalar() && !entity.hasLengthParameters() && !hlfir::isBoxAddressOrValueType(entity.getType())) return {fir::ExtendedValue{entity.getBase()}, llvm::None}; TODO(loc, "HLFIR variable to fir::ExtendedValue without a " "FortranVariableOpInterface"); } if (entity.getType().isa()) { hlfir::AssociateOp associate = hlfir::genAssociateExpr( loc, builder, entity, entity.getType(), "adapt.valuebyref"); auto *bldr = &builder; hlfir::CleanupFunction cleanup = [bldr, loc, associate]() -> void { bldr->create(loc, associate); }; hlfir::Entity temp{associate.getBase()}; return {translateToExtendedValue(loc, builder, temp).first, cleanup}; } return {{static_cast(entity)}, {}}; } mlir::Value hlfir::Entity::getFirBase() const { if (fir::FortranVariableOpInterface variable = getIfVariableInterface()) { if (auto declareOp = mlir::dyn_cast(variable.getOperation())) return declareOp.getOriginalBase(); if (auto associateOp = mlir::dyn_cast(variable.getOperation())) return associateOp.getFirBase(); } return getBase(); } fir::ExtendedValue hlfir::translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder, fir::FortranVariableOpInterface variable) { /// When going towards FIR, use the original base value to avoid /// introducing descriptors at runtime when they are not required. mlir::Value firBase = Entity{variable}.getFirBase(); if (variable.isPointer() || variable.isAllocatable()) TODO(variable->getLoc(), "pointer or allocatable " "FortranVariableOpInterface to extendedValue"); if (firBase.getType().isa()) return fir::BoxValue(firBase, getExplicitLbounds(variable), getExplicitTypeParams(variable)); if (variable.isCharacter()) { if (variable.isArray()) return fir::CharArrayBoxValue(firBase, variable.getExplicitCharLen(), getExplicitExtents(variable), getExplicitLbounds(variable)); if (auto boxCharType = firBase.getType().dyn_cast()) { auto unboxed = builder.create( loc, fir::ReferenceType::get(boxCharType.getEleTy()), builder.getIndexType(), firBase); return fir::CharBoxValue(unboxed.getResult(0), variable.getExplicitCharLen()); } return fir::CharBoxValue(firBase, variable.getExplicitCharLen()); } if (variable.isArray()) return fir::ArrayBoxValue(firBase, getExplicitExtents(variable), getExplicitLbounds(variable)); return firBase; } hlfir::EntityWithAttributes hlfir::genDeclare(mlir::Location loc, fir::FirOpBuilder &builder, const fir::ExtendedValue &exv, llvm::StringRef name, fir::FortranVariableFlagsAttr flags) { mlir::Value base = fir::getBase(exv); assert(fir::isa_passbyref_type(base.getType()) && "entity being declared must be in memory"); mlir::Value shapeOrShift; llvm::SmallVector lenParams; exv.match( [&](const fir::CharBoxValue &box) { lenParams.emplace_back(box.getLen()); }, [&](const fir::ArrayBoxValue &) { shapeOrShift = builder.createShape(loc, exv); }, [&](const fir::CharArrayBoxValue &box) { shapeOrShift = builder.createShape(loc, exv); lenParams.emplace_back(box.getLen()); }, [&](const fir::BoxValue &box) { if (!box.getLBounds().empty()) shapeOrShift = builder.createShape(loc, exv); lenParams.append(box.getExplicitParameters().begin(), box.getExplicitParameters().end()); }, [&](const fir::MutableBoxValue &box) { lenParams.append(box.nonDeferredLenParams().begin(), box.nonDeferredLenParams().end()); }, [](const auto &) {}); auto declareOp = builder.create( loc, base, name, shapeOrShift, lenParams, flags); return mlir::cast(declareOp.getOperation()); } hlfir::AssociateOp hlfir::genAssociateExpr(mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity value, mlir::Type variableType, llvm::StringRef name) { assert(value.isValue() && "must not be a variable"); mlir::Value shape{}; if (value.isArray()) TODO(loc, "associating array expressions"); mlir::Value source = value; // Lowered scalar expression values for numerical and logical may have a // different type than what is required for the type in memory (logical // expressions are typically manipulated as i1, but needs to be stored // according to the fir.logical so that the storage size is correct). // Character length mismatches are ignored (it is ok for one to be dynamic // and the other static). mlir::Type varEleTy = getFortranElementType(variableType); mlir::Type valueEleTy = getFortranElementType(value.getType()); if (varEleTy != valueEleTy && !(valueEleTy.isa() && varEleTy.isa())) { assert(value.isScalar() && fir::isa_trivial(value.getType())); source = builder.createConvert(loc, fir::unwrapPassByRefType(variableType), value); } llvm::SmallVector lenParams; genLengthParameters(loc, builder, value, lenParams); return builder.create(loc, source, name, shape, lenParams, fir::FortranVariableFlagsAttr{}); } mlir::Value hlfir::genVariableRawAddress(mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity var) { assert(var.isVariable() && "only address of variables can be taken"); mlir::Value baseAddr = var.getFirBase(); if (var.isMutableBox()) baseAddr = builder.create(loc, baseAddr); // Get raw address. if (baseAddr.getType().isa()) { auto addrType = fir::ReferenceType::get(fir::unwrapPassByRefType(baseAddr.getType())); baseAddr = builder.create(loc, addrType, baseAddr); } return baseAddr; } mlir::Value hlfir::genVariableBoxChar(mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity var) { assert(var.isVariable() && "only address of variables can be taken"); if (var.getType().isa()) return var; mlir::Value addr = genVariableRawAddress(loc, builder, var); llvm::SmallVector lengths; genLengthParameters(loc, builder, var, lengths); assert(lengths.size() == 1); auto charType = var.getFortranElementType().cast(); auto boxCharType = fir::BoxCharType::get(builder.getContext(), charType.getFKind()); auto scalarAddr = builder.createConvert(loc, fir::ReferenceType::get(charType), addr); return builder.create(loc, boxCharType, scalarAddr, lengths[0]); } hlfir::Entity hlfir::loadTrivialScalar(mlir::Location loc, fir::FirOpBuilder &builder, Entity entity) { if (entity.isVariable() && entity.isScalar() && fir::isa_trivial(entity.getFortranElementType())) { entity = derefPointersAndAllocatables(loc, builder, entity); return Entity{builder.create(loc, entity)}; } return entity; } static mlir::Value genUBound(mlir::Location loc, fir::FirOpBuilder &builder, mlir::Value lb, mlir::Value extent, mlir::Value one) { if (auto constantLb = fir::factory::getIntIfConstant(lb)) if (*constantLb == 1) return extent; extent = builder.createConvert(loc, one.getType(), extent); lb = builder.createConvert(loc, one.getType(), lb); auto add = builder.create(loc, lb, extent); return builder.create(loc, add, one); } llvm::SmallVector> hlfir::genBounds(mlir::Location loc, fir::FirOpBuilder &builder, Entity entity) { if (entity.getType().isa()) TODO(loc, "bounds of expressions in hlfir"); auto [exv, cleanup] = translateToExtendedValue(loc, builder, entity); assert(!cleanup && "translation of entity should not yield cleanup"); if (const auto *mutableBox = exv.getBoxOf()) exv = fir::factory::genMutableBoxRead(builder, loc, *mutableBox); mlir::Type idxTy = builder.getIndexType(); mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); llvm::SmallVector> result; for (unsigned dim = 0; dim < exv.rank(); ++dim) { mlir::Value extent = fir::factory::readExtent(builder, loc, exv, dim); mlir::Value lb = fir::factory::readLowerBound(builder, loc, exv, dim, one); mlir::Value ub = genUBound(loc, builder, lb, extent, one); result.push_back({lb, ub}); } return result; } void hlfir::genLengthParameters(mlir::Location loc, fir::FirOpBuilder &builder, Entity entity, llvm::SmallVectorImpl &result) { if (!entity.hasLengthParameters()) return; if (entity.getType().isa()) { mlir::Value expr = entity; if (auto reassoc = expr.getDefiningOp()) expr = reassoc.getVal(); // Going through fir::ExtendedValue would create a temp, // which is not desired for an inquiry. // TODO: make this an interface when adding further character producing ops. if (auto concat = expr.getDefiningOp()) { result.push_back(concat.getLength()); return; } else if (auto asExpr = expr.getDefiningOp()) { hlfir::genLengthParameters(loc, builder, hlfir::Entity{asExpr.getVar()}, result); return; } TODO(loc, "inquire type parameters of hlfir.expr"); } if (entity.isCharacter()) { auto [exv, cleanup] = translateToExtendedValue(loc, builder, entity); assert(!cleanup && "translation of entity should not yield cleanup"); result.push_back(fir::factory::readCharLen(builder, loc, exv)); return; } TODO(loc, "inquire PDTs length parameters in HLFIR"); } std::pair hlfir::genVariableFirBaseShapeAndParams( mlir::Location loc, fir::FirOpBuilder &builder, Entity entity, llvm::SmallVectorImpl &typeParams) { auto [exv, cleanup] = translateToExtendedValue(loc, builder, entity); assert(!cleanup && "variable to Exv should not produce cleanup"); if (entity.hasLengthParameters()) { auto params = fir::getTypeParams(exv); typeParams.append(params.begin(), params.end()); } if (entity.isScalar()) return {fir::getBase(exv), mlir::Value{}}; if (auto variableInterface = entity.getIfVariableInterface()) return {fir::getBase(exv), variableInterface.getShape()}; return {fir::getBase(exv), builder.createShape(loc, exv)}; } hlfir::Entity hlfir::derefPointersAndAllocatables(mlir::Location loc, fir::FirOpBuilder &builder, Entity entity) { if (entity.isMutableBox()) return hlfir::Entity{builder.create(loc, entity).getResult()}; return entity; }