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