Files
clang-p2996/flang/lib/Optimizer/Builder/HLFIRTools.cpp

343 lines
15 KiB
C++

//===-- 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<mlir::Value>
getExplicitExtents(fir::FortranVariableOpInterface var) {
llvm::SmallVector<mlir::Value> result;
if (mlir::Value shape = var.getShape()) {
auto *shapeOp = shape.getDefiningOp();
if (auto s = mlir::dyn_cast_or_null<fir::ShapeOp>(shapeOp)) {
auto e = s.getExtents();
result.append(e.begin(), e.end());
} else if (auto s = mlir::dyn_cast_or_null<fir::ShapeShiftOp>(shapeOp)) {
auto e = s.getExtents();
result.append(e.begin(), e.end());
} else if (mlir::dyn_cast_or_null<fir::ShiftOp>(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<mlir::Value>
getExplicitLbounds(fir::FortranVariableOpInterface var) {
llvm::SmallVector<mlir::Value> result;
if (mlir::Value shape = var.getShape()) {
auto *shapeOp = shape.getDefiningOp();
if (auto s = mlir::dyn_cast_or_null<fir::ShapeOp>(shapeOp)) {
return {};
} else if (auto s = mlir::dyn_cast_or_null<fir::ShapeShiftOp>(shapeOp)) {
auto e = s.getOrigins();
result.append(e.begin(), e.end());
} else if (auto s = mlir::dyn_cast_or_null<fir::ShiftOp>(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<mlir::Value>
getExplicitTypeParams(fir::FortranVariableOpInterface var) {
llvm::SmallVector<mlir::Value> res;
mlir::OperandRange range = var.getExplicitTypeParams();
res.append(range.begin(), range.end());
return res;
}
std::pair<fir::ExtendedValue, llvm::Optional<hlfir::CleanupFunction>>
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::ExprType>()) {
hlfir::AssociateOp associate = hlfir::genAssociateExpr(
loc, builder, entity, entity.getType(), "adapt.valuebyref");
auto *bldr = &builder;
hlfir::CleanupFunction cleanup = [bldr, loc, associate]() -> void {
bldr->create<hlfir::EndAssociateOp>(loc, associate);
};
hlfir::Entity temp{associate.getBase()};
return {translateToExtendedValue(loc, builder, temp).first, cleanup};
}
return {{static_cast<mlir::Value>(entity)}, {}};
}
mlir::Value hlfir::Entity::getFirBase() const {
if (fir::FortranVariableOpInterface variable = getIfVariableInterface()) {
if (auto declareOp =
mlir::dyn_cast<hlfir::DeclareOp>(variable.getOperation()))
return declareOp.getOriginalBase();
if (auto associateOp =
mlir::dyn_cast<hlfir::AssociateOp>(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<fir::BaseBoxType>())
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<fir::BoxCharType>()) {
auto unboxed = builder.create<fir::UnboxCharOp>(
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<mlir::Value> 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<hlfir::DeclareOp>(
loc, base, name, shapeOrShift, lenParams, flags);
return mlir::cast<fir::FortranVariableOpInterface>(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<kind> 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<fir::CharacterType>() &&
varEleTy.isa<fir::CharacterType>())) {
assert(value.isScalar() && fir::isa_trivial(value.getType()));
source = builder.createConvert(loc, fir::unwrapPassByRefType(variableType),
value);
}
llvm::SmallVector<mlir::Value> lenParams;
genLengthParameters(loc, builder, value, lenParams);
return builder.create<hlfir::AssociateOp>(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<fir::LoadOp>(loc, baseAddr);
// Get raw address.
if (baseAddr.getType().isa<fir::BaseBoxType>()) {
auto addrType =
fir::ReferenceType::get(fir::unwrapPassByRefType(baseAddr.getType()));
baseAddr = builder.create<fir::BoxAddrOp>(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<fir::BoxCharType>())
return var;
mlir::Value addr = genVariableRawAddress(loc, builder, var);
llvm::SmallVector<mlir::Value> lengths;
genLengthParameters(loc, builder, var, lengths);
assert(lengths.size() == 1);
auto charType = var.getFortranElementType().cast<fir::CharacterType>();
auto boxCharType =
fir::BoxCharType::get(builder.getContext(), charType.getFKind());
auto scalarAddr =
builder.createConvert(loc, fir::ReferenceType::get(charType), addr);
return builder.create<fir::EmboxCharOp>(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<fir::LoadOp>(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<mlir::arith::AddIOp>(loc, lb, extent);
return builder.create<mlir::arith::SubIOp>(loc, add, one);
}
llvm::SmallVector<std::pair<mlir::Value, mlir::Value>>
hlfir::genBounds(mlir::Location loc, fir::FirOpBuilder &builder,
Entity entity) {
if (entity.getType().isa<hlfir::ExprType>())
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<fir::MutableBoxValue>())
exv = fir::factory::genMutableBoxRead(builder, loc, *mutableBox);
mlir::Type idxTy = builder.getIndexType();
mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
llvm::SmallVector<std::pair<mlir::Value, mlir::Value>> 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<mlir::Value> &result) {
if (!entity.hasLengthParameters())
return;
if (entity.getType().isa<hlfir::ExprType>()) {
mlir::Value expr = entity;
if (auto reassoc = expr.getDefiningOp<hlfir::NoReassocOp>())
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<hlfir::ConcatOp>()) {
result.push_back(concat.getLength());
return;
} else if (auto asExpr = expr.getDefiningOp<hlfir::AsExprOp>()) {
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<mlir::Value, mlir::Value> hlfir::genVariableFirBaseShapeAndParams(
mlir::Location loc, fir::FirOpBuilder &builder, Entity entity,
llvm::SmallVectorImpl<mlir::Value> &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<fir::LoadOp>(loc, entity).getResult()};
return entity;
}