//===-- HlfirIntrinsics.h -- lowering to HLFIR intrinsic ops ----*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
//
//===----------------------------------------------------------------------===//
///
/// Implements lowering of transformational intrinsics to HLFIR intrinsic
/// operations
///
//===----------------------------------------------------------------------===//
#ifndef FORTRAN_LOWER_HLFIRINTRINSICS_H
#define FORTRAN_LOWER_HLFIRINTRINSICS_H
#include "flang/Optimizer/Builder/HLFIRTools.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/HLFIR/HLFIROps.h"
#include "llvm/ADT/SmallVector.h"
#include <cassert>
#include <optional>
#include <string>
namespace mlir {
class Location;
class Type;
class Value;
class ValueRange;
} // namespace mlir
namespace fir {
class FirOpBuilder;
struct IntrinsicArgumentLoweringRules;
} // namespace fir
namespace Fortran::lower {
/// This structure holds the initial lowered value of an actual argument that
/// was lowered regardless of the interface, and it holds whether or not it
/// may be absent at runtime and the dummy is optional.
struct PreparedActualArgument {
PreparedActualArgument(hlfir::Entity actual,
std::optional<mlir::Value> isPresent)
: actual{actual}, isPresent{isPresent} {}
PreparedActualArgument(hlfir::ElementalAddrOp vectorSubscriptedActual)
: actual{vectorSubscriptedActual}, isPresent{std::nullopt} {}
void setElementalIndices(mlir::ValueRange &indices) {
oneBasedElementalIndices = &indices;
}
/// Get the prepared actual. If this is an array argument in an elemental
/// call, the current element value will be returned.
hlfir::Entity getActual(mlir::Location loc, fir::FirOpBuilder &builder) const;
void derefPointersAndAllocatables(mlir::Location loc,
fir::FirOpBuilder &builder) {
if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual))
actual = hlfir::derefPointersAndAllocatables(loc, builder, *actualEntity);
}
void loadTrivialScalar(mlir::Location loc, fir::FirOpBuilder &builder) {
if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual))
actual = hlfir::loadTrivialScalar(loc, builder, *actualEntity);
}
/// Ensure an array expression argument is fully evaluated in memory before
/// the call. Useful for impure elemental calls.
hlfir::AssociateOp associateIfArrayExpr(mlir::Location loc,
fir::FirOpBuilder &builder) {
if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual)) {
if (!actualEntity->isVariable() && actualEntity->isArray()) {
mlir::Type storageType = actualEntity->getType();
hlfir::AssociateOp associate = hlfir::genAssociateExpr(
loc, builder, *actualEntity, storageType, "adapt.impure_arg_eval");
actual = hlfir::Entity{associate};
return associate;
}
}
return {};
}
bool isArray() const {
return std::holds_alternative<hlfir::ElementalAddrOp>(actual) ||
std::get<hlfir::Entity>(actual).isArray();
}
mlir::Value genShape(mlir::Location loc, fir::FirOpBuilder &builder) {
if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual))
return hlfir::genShape(loc, builder, *actualEntity);
return std::get<hlfir::ElementalAddrOp>(actual).getShape();
}
mlir::Value genCharLength(mlir::Location loc, fir::FirOpBuilder &builder) {
if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual))
return hlfir::genCharLength(loc, builder, *actualEntity);
auto typeParams = std::get<hlfir::ElementalAddrOp>(actual).getTypeparams();
assert(typeParams.size() == 1 &&
"failed to retrieve vector subscripted character length");
return typeParams[0];
}
/// When the argument is polymorphic, get mold value with the same dynamic
/// type.
mlir::Value getPolymorphicMold(mlir::Location loc) const {
if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual))
return *actualEntity;
TODO(loc, "polymorphic vector subscripts");
}
bool handleDynamicOptional() const { return isPresent.has_value(); }
mlir::Value getIsPresent() const {
assert(handleDynamicOptional() && "not a dynamic optional");
return *isPresent;
}
void resetOptionalAspect() { isPresent = std::nullopt; }
private:
std::variant<hlfir::Entity, hlfir::ElementalAddrOp> actual;
mlir::ValueRange *oneBasedElementalIndices{nullptr};
// When the actual may be dynamically optional, "isPresent"
// holds a boolean value indicating the presence of the
// actual argument at runtime.
std::optional<mlir::Value> isPresent;
};
/// Vector of pre-lowered actual arguments. nullopt if the actual is
/// "statically" absent (if it was not syntactically provided).
using PreparedActualArguments =
llvm::SmallVector<std::optional<PreparedActualArgument>>;
std::optional<hlfir::EntityWithAttributes> lowerHlfirIntrinsic(
fir::FirOpBuilder &builder, mlir::Location loc, const std::string &name,
const Fortran::lower::PreparedActualArguments &loweredActuals,
const fir::IntrinsicArgumentLoweringRules *argLowering,
mlir::Type stmtResultType);
} // namespace Fortran::lower
#endif // FORTRAN_LOWER_HLFIRINTRINSICS_H