mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-17 17:26:41 +00:00

Add `c_devloc` as intrinsic and inline it during lowering. `c_devloc` is used in CUDA Fortran to get the address of device variables. For the moment, we borrow almost all semantic checks from `c_loc` except for the pointer or target restriction. The specifications of `c_devloc` are are pretty vague and we will relax/enforce the restrictions based on library and apps usage comparing them to the reference compiler.
1743 lines
76 KiB
C++
1743 lines
76 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/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::getNamedFunction(mlir::ModuleOp modOp,
|
|
const mlir::SymbolTable *symbolTable,
|
|
llvm::StringRef name) {
|
|
if (symbolTable)
|
|
if (auto func = symbolTable->lookup<mlir::func::FuncOp>(name)) {
|
|
#ifdef EXPENSIVE_CHECKS
|
|
assert(func == modOp.lookupSymbol<mlir::func::FuncOp>(name) &&
|
|
"symbolTable and module out of sync");
|
|
#endif
|
|
return func;
|
|
}
|
|
return modOp.lookupSymbol<mlir::func::FuncOp>(name);
|
|
}
|
|
|
|
mlir::func::FuncOp
|
|
fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp,
|
|
const mlir::SymbolTable *symbolTable,
|
|
mlir::SymbolRefAttr symbol) {
|
|
if (symbolTable)
|
|
if (auto func = symbolTable->lookup<mlir::func::FuncOp>(
|
|
symbol.getLeafReference())) {
|
|
#ifdef EXPENSIVE_CHECKS
|
|
assert(func == modOp.lookupSymbol<mlir::func::FuncOp>(symbol) &&
|
|
"symbolTable and module out of sync");
|
|
#endif
|
|
return func;
|
|
}
|
|
return modOp.lookupSymbol<mlir::func::FuncOp>(symbol);
|
|
}
|
|
|
|
fir::GlobalOp
|
|
fir::FirOpBuilder::getNamedGlobal(mlir::ModuleOp modOp,
|
|
const mlir::SymbolTable *symbolTable,
|
|
llvm::StringRef name) {
|
|
if (symbolTable)
|
|
if (auto global = symbolTable->lookup<fir::GlobalOp>(name)) {
|
|
#ifdef EXPENSIVE_CHECKS
|
|
assert(global == modOp.lookupSymbol<fir::GlobalOp>(name) &&
|
|
"symbolTable and module out of sync");
|
|
#endif
|
|
return global;
|
|
}
|
|
return modOp.lookupSymbol<fir::GlobalOp>(name);
|
|
}
|
|
|
|
mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy) {
|
|
assert(!mlir::isa<fir::ReferenceType>(eleTy) && "cannot be a reference type");
|
|
return fir::ReferenceType::get(eleTy);
|
|
}
|
|
|
|
mlir::Type fir::FirOpBuilder::getVarLenSeqTy(mlir::Type eleTy, unsigned rank) {
|
|
fir::SequenceType::Shape shape(rank, fir::SequenceType::getUnknownExtent());
|
|
return fir::SequenceType::get(shape, eleTy);
|
|
}
|
|
|
|
mlir::Type fir::FirOpBuilder::getRealType(int kind) {
|
|
switch (kindMap.getRealTypeID(kind)) {
|
|
case llvm::Type::TypeID::HalfTyID:
|
|
return mlir::FloatType::getF16(getContext());
|
|
case llvm::Type::TypeID::BFloatTyID:
|
|
return mlir::FloatType::getBF16(getContext());
|
|
case llvm::Type::TypeID::FloatTyID:
|
|
return mlir::FloatType::getF32(getContext());
|
|
case llvm::Type::TypeID::DoubleTyID:
|
|
return mlir::FloatType::getF64(getContext());
|
|
case llvm::Type::TypeID::X86_FP80TyID:
|
|
return mlir::FloatType::getF80(getContext());
|
|
case llvm::Type::TypeID::FP128TyID:
|
|
return mlir::FloatType::getF128(getContext());
|
|
default:
|
|
fir::emitFatalError(mlir::UnknownLoc::get(getContext()),
|
|
"unsupported type !fir.real<kind>");
|
|
}
|
|
}
|
|
|
|
mlir::Value fir::FirOpBuilder::createNullConstant(mlir::Location loc,
|
|
mlir::Type ptrType) {
|
|
auto ty = ptrType ? ptrType : getRefType(getNoneType());
|
|
return create<fir::ZeroOp>(loc, ty);
|
|
}
|
|
|
|
mlir::Value fir::FirOpBuilder::createIntegerConstant(mlir::Location loc,
|
|
mlir::Type ty,
|
|
std::int64_t cst) {
|
|
assert((cst >= 0 || mlir::isa<mlir::IndexType>(ty) ||
|
|
mlir::cast<mlir::IntegerType>(ty).getWidth() <= 64) &&
|
|
"must use APint");
|
|
return create<mlir::arith::ConstantOp>(loc, ty, getIntegerAttr(ty, cst));
|
|
}
|
|
|
|
mlir::Value fir::FirOpBuilder::createAllOnesInteger(mlir::Location loc,
|
|
mlir::Type ty) {
|
|
if (mlir::isa<mlir::IndexType>(ty))
|
|
return createIntegerConstant(loc, ty, -1);
|
|
llvm::APInt allOnes =
|
|
llvm::APInt::getAllOnes(mlir::cast<mlir::IntegerType>(ty).getWidth());
|
|
return create<mlir::arith::ConstantOp>(loc, ty, getIntegerAttr(ty, allOnes));
|
|
}
|
|
|
|
mlir::Value
|
|
fir::FirOpBuilder::createRealConstant(mlir::Location loc, mlir::Type fltTy,
|
|
llvm::APFloat::integerPart val) {
|
|
auto apf = [&]() -> llvm::APFloat {
|
|
if (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) {
|
|
if (fir::hasDynamicSize(recordType))
|
|
return false;
|
|
for (auto [_, fieldType] : recordType.getTypeList()) {
|
|
// Derived type component may have user assignment (so far, we cannot tell
|
|
// in FIR, so assume it is always the case, TODO: get the actual info).
|
|
if (mlir::isa<fir::RecordType>(fir::unwrapSequenceType(fieldType)))
|
|
return false;
|
|
// Allocatable components need deep copy.
|
|
if (auto boxType = mlir::dyn_cast<fir::BaseBoxType>(fieldType))
|
|
if (mlir::isa<fir::HeapType>(boxType.getEleTy()))
|
|
return false;
|
|
}
|
|
// Constant size components without user defined assignment and pointers can
|
|
// be memcopied.
|
|
return true;
|
|
}
|
|
|
|
static bool mayHaveFinalizer(fir::RecordType recordType,
|
|
fir::FirOpBuilder &builder) {
|
|
if (auto typeInfo = builder.getModule().lookupSymbol<fir::TypeInfoOp>(
|
|
recordType.getName()))
|
|
return !typeInfo.getNoFinal();
|
|
// No info, be pessimistic.
|
|
return true;
|
|
}
|
|
|
|
void fir::factory::genRecordAssignment(fir::FirOpBuilder &builder,
|
|
mlir::Location loc,
|
|
const fir::ExtendedValue &lhs,
|
|
const fir::ExtendedValue &rhs,
|
|
bool needFinalization,
|
|
bool isTemporaryLHS) {
|
|
assert(lhs.rank() == 0 && rhs.rank() == 0 && "assume scalar assignment");
|
|
auto baseTy = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(lhs).getType());
|
|
assert(baseTy && "must be a memory type");
|
|
// Box operands may be polymorphic, it is not entirely clear from 10.2.1.3
|
|
// if the assignment is performed on the dynamic of declared type. Use the
|
|
// runtime assuming it is performed on the dynamic type.
|
|
bool hasBoxOperands =
|
|
mlir::isa<fir::BaseBoxType>(fir::getBase(lhs).getType()) ||
|
|
mlir::isa<fir::BaseBoxType>(fir::getBase(rhs).getType());
|
|
auto recTy = mlir::dyn_cast<fir::RecordType>(baseTy);
|
|
assert(recTy && "must be a record type");
|
|
if ((needFinalization && mayHaveFinalizer(recTy, builder)) ||
|
|
hasBoxOperands || !recordTypeCanBeMemCopied(recTy)) {
|
|
auto to = fir::getBase(builder.createBox(loc, lhs));
|
|
auto from = fir::getBase(builder.createBox(loc, rhs));
|
|
// The runtime entry point may modify the LHS descriptor if it is
|
|
// an allocatable. Allocatable assignment is handle elsewhere in lowering,
|
|
// so just create a fir.ref<fir.box<>> from the fir.box to comply with the
|
|
// runtime interface, but assume the fir.box is unchanged.
|
|
// TODO: does this holds true with polymorphic entities ?
|
|
auto toMutableBox = builder.createTemporary(loc, to.getType());
|
|
builder.create<fir::StoreOp>(loc, to, toMutableBox);
|
|
if (isTemporaryLHS)
|
|
fir::runtime::genAssignTemporary(builder, loc, toMutableBox, from);
|
|
else
|
|
fir::runtime::genAssign(builder, loc, toMutableBox, from);
|
|
return;
|
|
}
|
|
|
|
// Otherwise, the derived type has compile time constant size and for which
|
|
// the component by component assignment can be replaced by a memory copy.
|
|
// Since we do not know the size of the derived type in lowering, do a
|
|
// component by component assignment. Note that a single fir.load/fir.store
|
|
// could be used on "small" record types, but as the type size grows, this
|
|
// leads to issues in LLVM (long compile times, long IR files, and even
|
|
// asserts at some point). Since there is no good size boundary, just always
|
|
// use component by component assignment here.
|
|
genComponentByComponentAssignment(builder, loc, lhs, rhs, isTemporaryLHS);
|
|
}
|
|
|
|
mlir::TupleType
|
|
fir::factory::getRaggedArrayHeaderType(fir::FirOpBuilder &builder) {
|
|
mlir::IntegerType i64Ty = builder.getIntegerType(64);
|
|
auto arrTy = fir::SequenceType::get(builder.getIntegerType(8), 1);
|
|
auto buffTy = fir::HeapType::get(arrTy);
|
|
auto extTy = fir::SequenceType::get(i64Ty, 1);
|
|
auto shTy = fir::HeapType::get(extTy);
|
|
return mlir::TupleType::get(builder.getContext(), {i64Ty, buffTy, shTy});
|
|
}
|
|
|
|
mlir::Value fir::factory::genLenOfCharacter(
|
|
fir::FirOpBuilder &builder, mlir::Location loc, fir::ArrayLoadOp arrLoad,
|
|
llvm::ArrayRef<mlir::Value> path, llvm::ArrayRef<mlir::Value> substring) {
|
|
llvm::SmallVector<mlir::Value> typeParams(arrLoad.getTypeparams());
|
|
return genLenOfCharacter(builder, loc,
|
|
mlir::cast<fir::SequenceType>(arrLoad.getType()),
|
|
arrLoad.getMemref(), typeParams, path, substring);
|
|
}
|
|
|
|
mlir::Value fir::factory::genLenOfCharacter(
|
|
fir::FirOpBuilder &builder, mlir::Location loc, fir::SequenceType seqTy,
|
|
mlir::Value memref, llvm::ArrayRef<mlir::Value> typeParams,
|
|
llvm::ArrayRef<mlir::Value> path, llvm::ArrayRef<mlir::Value> substring) {
|
|
auto idxTy = builder.getIndexType();
|
|
auto zero = builder.createIntegerConstant(loc, idxTy, 0);
|
|
auto saturatedDiff = [&](mlir::Value lower, mlir::Value upper) {
|
|
auto diff = builder.create<mlir::arith::SubIOp>(loc, upper, lower);
|
|
auto one = builder.createIntegerConstant(loc, idxTy, 1);
|
|
auto size = builder.create<mlir::arith::AddIOp>(loc, diff, one);
|
|
auto cmp = builder.create<mlir::arith::CmpIOp>(
|
|
loc, mlir::arith::CmpIPredicate::sgt, size, zero);
|
|
return builder.create<mlir::arith::SelectOp>(loc, cmp, size, zero);
|
|
};
|
|
if (substring.size() == 2) {
|
|
auto upper = builder.createConvert(loc, idxTy, substring.back());
|
|
auto lower = builder.createConvert(loc, idxTy, substring.front());
|
|
return saturatedDiff(lower, upper);
|
|
}
|
|
auto lower = zero;
|
|
if (substring.size() == 1)
|
|
lower = builder.createConvert(loc, idxTy, substring.front());
|
|
auto eleTy = fir::applyPathToType(seqTy, path);
|
|
if (!fir::hasDynamicSize(eleTy)) {
|
|
if (auto charTy = mlir::dyn_cast<fir::CharacterType>(eleTy)) {
|
|
// Use LEN from the type.
|
|
return builder.createIntegerConstant(loc, idxTy, charTy.getLen());
|
|
}
|
|
// Do we need to support !fir.array<!fir.char<k,n>>?
|
|
fir::emitFatalError(loc,
|
|
"application of path did not result in a !fir.char");
|
|
}
|
|
if (fir::isa_box_type(memref.getType())) {
|
|
if (mlir::isa<fir::BoxCharType>(memref.getType()))
|
|
return builder.create<fir::BoxCharLenOp>(loc, idxTy, memref);
|
|
if (mlir::isa<fir::BoxType>(memref.getType()))
|
|
return CharacterExprHelper(builder, loc).readLengthFromBox(memref);
|
|
fir::emitFatalError(loc, "memref has wrong type");
|
|
}
|
|
if (typeParams.empty()) {
|
|
fir::emitFatalError(loc, "array_load must have typeparams");
|
|
}
|
|
if (fir::isa_char(seqTy.getEleTy())) {
|
|
assert(typeParams.size() == 1 && "too many typeparams");
|
|
return typeParams.front();
|
|
}
|
|
TODO(loc, "LEN of character must be computed at runtime");
|
|
}
|
|
|
|
mlir::Value fir::factory::createZeroValue(fir::FirOpBuilder &builder,
|
|
mlir::Location loc, mlir::Type type) {
|
|
mlir::Type i1 = builder.getIntegerType(1);
|
|
if (mlir::isa<fir::LogicalType>(type) || type == i1)
|
|
return builder.createConvert(loc, type, builder.createBool(loc, false));
|
|
if (fir::isa_integer(type))
|
|
return builder.createIntegerConstant(loc, type, 0);
|
|
if (fir::isa_real(type))
|
|
return builder.createRealZeroConstant(loc, type);
|
|
if (fir::isa_complex(type)) {
|
|
fir::factory::Complex complexHelper(builder, loc);
|
|
mlir::Type partType = complexHelper.getComplexPartType(type);
|
|
mlir::Value zeroPart = builder.createRealZeroConstant(loc, partType);
|
|
return complexHelper.createComplex(type, zeroPart, zeroPart);
|
|
}
|
|
fir::emitFatalError(loc, "internal: trying to generate zero value of non "
|
|
"numeric or logical type");
|
|
}
|
|
|
|
std::optional<std::int64_t>
|
|
fir::factory::getExtentFromTriplet(mlir::Value lb, mlir::Value ub,
|
|
mlir::Value stride) {
|
|
std::function<std::optional<std::int64_t>(mlir::Value)> getConstantValue =
|
|
[&](mlir::Value value) -> std::optional<std::int64_t> {
|
|
if (auto valInt = fir::getIntIfConstant(value))
|
|
return *valInt;
|
|
auto *definingOp = value.getDefiningOp();
|
|
if (mlir::isa_and_nonnull<fir::ConvertOp>(definingOp)) {
|
|
auto valOp = mlir::dyn_cast<fir::ConvertOp>(definingOp);
|
|
return getConstantValue(valOp.getValue());
|
|
}
|
|
return {};
|
|
};
|
|
if (auto lbInt = getConstantValue(lb)) {
|
|
if (auto ubInt = getConstantValue(ub)) {
|
|
if (auto strideInt = getConstantValue(stride)) {
|
|
if (*strideInt != 0) {
|
|
std::int64_t extent = 1 + (*ubInt - *lbInt) / *strideInt;
|
|
if (extent > 0)
|
|
return extent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
mlir::Value fir::factory::genMaxWithZero(fir::FirOpBuilder &builder,
|
|
mlir::Location loc,
|
|
mlir::Value value) {
|
|
mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0);
|
|
if (mlir::Operation *definingOp = value.getDefiningOp())
|
|
if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp))
|
|
if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(cst.getValue()))
|
|
return intAttr.getInt() > 0 ? value : zero;
|
|
mlir::Value valueIsGreater = builder.create<mlir::arith::CmpIOp>(
|
|
loc, mlir::arith::CmpIPredicate::sgt, value, zero);
|
|
return builder.create<mlir::arith::SelectOp>(loc, valueIsGreater, value,
|
|
zero);
|
|
}
|
|
|
|
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;
|
|
}
|