[flang] Upstreaming intrinsic call lowering.

This module implements the lowering of Fortran intrinsics to the
corresponding calls in support libraries (the Fortran runtime, math
libraries, etc.)

This revision is a tad larger because there are a large number of Fortran
intrinsics and this adds lowering for a fair number of them.

Differential revision: https://reviews.llvm.org/D83355
This commit is contained in:
Eric Schweitz 2020-07-07 15:39:09 -07:00
parent 15fa287b64
commit 24b62f28c5
9 changed files with 1595 additions and 104 deletions

View File

@ -106,6 +106,19 @@ public:
/// Character lengths. TODO: move this to FirOpBuilder?
mlir::Type getLengthType() { return builder.getIndexType(); }
/// Create an extended value from:
/// - fir.boxchar<kind>
/// - fir.ref<fir.array<len x fir.char<kind>>>
/// - fir.array<len x fir.char<kind>>
/// - fir.char<kind>
/// - fir.ref<char<kind>>
/// If the no length is passed, it is attempted to be extracted from \p
/// character (or its type). This will crash if this is not possible.
/// The returned value is a CharBoxValue if \p character is a scalar,
/// otherwise it is a CharArrayBoxValue.
fir::ExtendedValue toExtendedValue(mlir::Value character,
mlir::Value len = {});
private:
fir::CharBoxValue materializeValue(const fir::CharBoxValue &str);
fir::CharBoxValue toDataLengthPair(mlir::Value character);

View File

@ -27,39 +27,40 @@ namespace Fortran::lower {
/// Helper for building calls to intrinsic functions in the runtime support
/// libraries.
class IntrinsicCallOpsHelper {
public:
explicit IntrinsicCallOpsHelper(FirOpBuilder &builder, mlir::Location loc)
: builder(builder), loc(loc) {}
IntrinsicCallOpsHelper(const IntrinsicCallOpsHelper &) = delete;
/// Generate the FIR+MLIR operations for the generic intrinsic \p name
/// with arguments \p args and expected result type \p resultType.
/// Returned mlir::Value is the returned Fortran intrinsic value.
fir::ExtendedValue genIntrinsicCall(llvm::StringRef name,
mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue> args);
/// Generate the FIR+MLIR operations for the generic intrinsic \p name
/// with arguments \p args and expected result type \p resultType.
/// Returned mlir::Value is the returned Fortran intrinsic value.
fir::ExtendedValue genIntrinsicCall(FirOpBuilder &, mlir::Location,
llvm::StringRef name, mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue> args);
//===--------------------------------------------------------------------===//
// Direct access to intrinsics that may be used by lowering outside
// of intrinsic call lowering.
//===--------------------------------------------------------------------===//
/// Get SymbolRefAttr of runtime (or wrapper function containing inlined
// implementation) of an unrestricted intrinsic (defined by its signature
// and generic name)
mlir::SymbolRefAttr
getUnrestrictedIntrinsicSymbolRefAttr(FirOpBuilder &, mlir::Location,
llvm::StringRef name,
mlir::FunctionType signature);
/// Generate maximum. There must be at least one argument and all arguments
/// must have the same type.
mlir::Value genMax(llvm::ArrayRef<mlir::Value> args);
//===--------------------------------------------------------------------===//
// Direct access to intrinsics that may be used by lowering outside
// of intrinsic call lowering.
//===--------------------------------------------------------------------===//
/// Generate minimum. Same constraints as genMax.
mlir::Value genMin(llvm::ArrayRef<mlir::Value> args);
/// Generate maximum. There must be at least one argument and all arguments
/// must have the same type.
mlir::Value genMax(FirOpBuilder &, mlir::Location,
llvm::ArrayRef<mlir::Value> args);
/// Generate power function x**y with given the expected
/// result type.
mlir::Value genPow(mlir::Type resultType, mlir::Value x, mlir::Value y);
/// Generate minimum. Same constraints as genMax.
mlir::Value genMin(FirOpBuilder &, mlir::Location,
llvm::ArrayRef<mlir::Value> args);
private:
FirOpBuilder &builder;
mlir::Location loc;
};
/// Generate power function x**y with given the expected
/// result type.
mlir::Value genPow(FirOpBuilder &, mlir::Location, mlir::Type resultType,
mlir::Value x, mlir::Value y);
} // namespace Fortran::lower

View File

@ -5,19 +5,32 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
//
//===----------------------------------------------------------------------===//
#ifndef FORTRAN_LOWER_MANGLER_H_
#define FORTRAN_LOWER_MANGLER_H_
#ifndef FORTRAN_LOWER_MANGLER_H
#define FORTRAN_LOWER_MANGLER_H
#include "mlir/IR/StandardTypes.h"
#include "llvm/ADT/StringRef.h"
#include <string>
namespace fir {
struct NameUniquer;
}
namespace llvm {
class StringRef;
}
/// Returns a name suitable to define mlir functions for Fortran intrinsic
/// Procedure. These names are guaranteed to not conflict with user defined
/// procedures. This is needed to implement Fortran generic intrinsics as
/// several mlir functions specialized for the argument types.
/// The result is guaranteed to be distinct for different mlir::FunctionType
/// arguments. The mangling pattern is:
/// fir.<generic name>.<result type>.<arg type>...
/// e.g ACOS(COMPLEX(4)) is mangled as fir.acos.z4.z4
std::string mangleIntrinsicProcedure(llvm::StringRef genericName,
mlir::FunctionType);
} // namespace fir
namespace Fortran {
namespace common {
@ -41,4 +54,4 @@ std::string demangleName(llvm::StringRef name);
} // namespace lower
} // namespace Fortran
#endif // FORTRAN_LOWER_MANGLER_H_
#endif // FORTRAN_LOWER_MANGLER_H

View File

@ -242,7 +242,7 @@ public:
static bool kindof(unsigned kind) { return kind == TypeKind::FIR_DIMS; }
/// returns -1 if the rank is unknown
int getRank() const;
unsigned getRank() const;
};
/// The type of a field name. Implementations may defer the layout of a Fortran
@ -437,6 +437,12 @@ inline bool isa_real(mlir::Type t) {
return t.isa<fir::RealType>() || t.isa<mlir::FloatType>();
}
/// Is `t` an integral type?
inline bool isa_integer(mlir::Type t) {
return t.isa<mlir::IndexType>() || t.isa<mlir::IntegerType>() ||
t.isa<fir::IntType>();
}
/// Is `t` a FIR or MLIR Complex type?
inline bool isa_complex(mlir::Type t) {
return t.isa<fir::CplxType>() || t.isa<mlir::ComplexType>();

View File

@ -9,6 +9,7 @@ add_flang_library(FortranLower
ConvertType.cpp
DoLoopHelper.cpp
FIRBuilder.cpp
IntrinsicCall.cpp
IO.cpp
Mangler.cpp
OpenMP.cpp

View File

@ -21,8 +21,10 @@ static fir::CharacterType getCharacterType(mlir::Type type) {
return boxType.getEleTy();
if (auto refType = type.dyn_cast<fir::ReferenceType>())
type = refType.getEleTy();
if (auto seqType = type.dyn_cast<fir::SequenceType>())
if (auto seqType = type.dyn_cast<fir::SequenceType>()) {
assert(seqType.getShape().size() == 1 && "rank must be 1");
type = seqType.getEleTy();
}
if (auto charType = type.dyn_cast<fir::CharacterType>())
return charType;
llvm_unreachable("Invalid character value type");
@ -65,38 +67,66 @@ fir::CharBoxValue Fortran::lower::CharacterExprHelper::materializeValue(
fir::CharBoxValue
Fortran::lower::CharacterExprHelper::toDataLengthPair(mlir::Value character) {
// TODO: get rid of toDataLengthPair when adding support for arrays
auto charBox = toExtendedValue(character).getCharBox();
assert(charBox && "Array unsupported in character lowering helper");
return *charBox;
}
fir::ExtendedValue
Fortran::lower::CharacterExprHelper::toExtendedValue(mlir::Value character,
mlir::Value len) {
auto lenType = getLengthType();
auto type = character.getType();
if (auto boxCharType = type.dyn_cast<fir::BoxCharType>()) {
auto base = character;
mlir::Value resultLen = len;
llvm::SmallVector<mlir::Value, 2> extents;
if (auto refType = type.dyn_cast<fir::ReferenceType>())
type = refType.getEleTy();
if (auto arrayType = type.dyn_cast<fir::SequenceType>()) {
type = arrayType.getEleTy();
auto shape = arrayType.getShape();
auto cstLen = shape[0];
if (!resultLen && cstLen != fir::SequenceType::getUnknownExtent())
resultLen = builder.createIntegerConstant(loc, lenType, cstLen);
// FIXME: only allow `?` in last dimension ?
auto typeExtents =
llvm::ArrayRef<fir::SequenceType::Extent>{shape}.drop_front();
auto indexType = builder.getIndexType();
for (auto extent : typeExtents) {
if (extent == fir::SequenceType::getUnknownExtent())
break;
extents.emplace_back(
builder.createIntegerConstant(loc, indexType, extent));
}
// Last extent might be missing in case of assumed-size. If more extents
// could not be deduced from type, that's an error (a fir.box should
// have been used in the interface).
if (extents.size() + 1 < typeExtents.size())
mlir::emitError(loc, "cannot retrieve array extents from type");
} else if (type.isa<fir::CharacterType>()) {
if (!resultLen)
resultLen = builder.createIntegerConstant(loc, lenType, 1);
} else if (auto boxCharType = type.dyn_cast<fir::BoxCharType>()) {
auto refType = builder.getRefType(boxCharType.getEleTy());
auto unboxed =
builder.create<fir::UnboxCharOp>(loc, refType, lenType, character);
return {unboxed.getResult(0), unboxed.getResult(1)};
base = unboxed.getResult(0);
if (!resultLen)
resultLen = unboxed.getResult(1);
} else if (type.isa<fir::BoxType>()) {
mlir::emitError(loc, "descriptor or derived type not yet handled");
} else {
llvm_unreachable("Cannot translate mlir::Value to character ExtendedValue");
}
if (auto seqType = type.dyn_cast<fir::CharacterType>()) {
// Materialize length for usage into character manipulations.
auto len = builder.createIntegerConstant(loc, lenType, 1);
return {character, len};
}
if (auto refType = type.dyn_cast<fir::ReferenceType>())
type = refType.getEleTy();
if (auto seqType = type.dyn_cast<fir::SequenceType>()) {
assert(seqType.hasConstantShape() &&
"ssa array value must have constant length");
auto shape = seqType.getShape();
assert(shape.size() == 1 && "only scalar character supported");
// Materialize length for usage into character manipulations.
auto len = builder.createIntegerConstant(loc, lenType, shape[0]);
// FIXME: this seems to work for tests, but don't think it is correct
if (auto load = dyn_cast<fir::LoadOp>(character.getDefiningOp()))
return {load.memref(), len};
return {character, len};
}
if (auto charTy = type.dyn_cast<fir::CharacterType>()) {
auto len = builder.createIntegerConstant(loc, lenType, 1);
return {character, len};
}
llvm::report_fatal_error("unexpected character type");
if (!resultLen)
mlir::emitError(loc, "no dynamic length found for character");
if (!extents.empty())
return fir::CharArrayBoxValue{base, resultLen, extents};
return fir::CharBoxValue{base, resultLen};
}
/// Get fir.ref<fir.char<kind>> type.
@ -115,17 +145,15 @@ Fortran::lower::CharacterExprHelper::createEmbox(const fir::CharBoxValue &box) {
auto boxCharType = fir::BoxCharType::get(builder.getContext(), kind);
auto refType = getReferenceType(str);
// So far, fir.emboxChar fails lowering to llvm when it is given
// fir.data<fir.array<len x fir.char<kind>>> types, so convert to
// fir.data<fir.char<kind>> if needed.
// fir.ref<fir.array<len x fir.char<kind>>> types, so convert to
// fir.ref<fir.char<kind>> if needed.
auto buff = str.getBuffer();
if (refType != str.getBuffer().getType())
buff = builder.createConvert(loc, refType, buff);
buff = builder.createConvert(loc, refType, buff);
// Convert in case the provided length is not of the integer type that must
// be used in boxchar.
auto lenType = getLengthType();
auto len = str.getLen();
if (str.getLen().getType() != lenType)
len = builder.createConvert(loc, lenType, len);
len = builder.createConvert(loc, lenType, len);
return builder.create<fir::EmboxCharOp>(loc, boxCharType, buff, len);
}
@ -182,16 +210,20 @@ Fortran::lower::CharacterExprHelper::createTemp(mlir::Type type,
void Fortran::lower::CharacterExprHelper::createLengthOneAssign(
const fir::CharBoxValue &lhs, const fir::CharBoxValue &rhs) {
auto addr = lhs.getBuffer();
auto refType = getReferenceType(lhs);
addr = builder.createConvert(loc, refType, addr);
auto val = rhs.getBuffer();
if (!needToMaterialize(rhs)) {
mlir::Value rhsAddr = rhs.getBuffer();
rhsAddr = builder.createConvert(loc, refType, rhsAddr);
val = builder.create<fir::LoadOp>(loc, rhsAddr);
// If rhs value resides in memory, load it.
if (!needToMaterialize(rhs))
val = builder.create<fir::LoadOp>(loc, val);
auto valTy = val.getType();
// Precondition is rhs is size 1, but it may be wrapped in a fir.array.
if (auto seqTy = valTy.dyn_cast<fir::SequenceType>()) {
auto zero = builder.createIntegerConstant(loc, builder.getIndexType(), 0);
valTy = seqTy.getEleTy();
val = builder.create<fir::ExtractValueOp>(loc, valTy, val, zero);
}
auto addrTy = fir::ReferenceType::get(valTy);
addr = builder.createConvert(loc, addrTy, addr);
assert(fir::dyn_cast_ptrEleTy(addr.getType()) == val.getType());
builder.create<fir::StoreOp>(loc, val, addr);
}
@ -211,8 +243,8 @@ void Fortran::lower::CharacterExprHelper::createAssign(
// if needed.
mlir::Value copyCount = lhs.getLen();
if (!compileTimeSameLength)
copyCount = Fortran::lower::IntrinsicCallOpsHelper{builder, loc}.genMin(
{lhs.getLen(), rhs.getLen()});
copyCount =
Fortran::lower::genMin(builder, loc, {lhs.getLen(), rhs.getLen()});
fir::CharBoxValue safeRhs = rhs;
if (needToMaterialize(rhs)) {
@ -433,7 +465,8 @@ Fortran::lower::CharacterExprHelper::materializeCharacter(mlir::Value str) {
bool Fortran::lower::CharacterExprHelper::isCharacterLiteral(mlir::Type type) {
if (auto seqType = type.dyn_cast<fir::SequenceType>())
return seqType.getEleTy().isa<fir::CharacterType>();
return (seqType.getShape().size() == 1) &&
seqType.getEleTy().isa<fir::CharacterType>();
return false;
}
@ -442,9 +475,9 @@ bool Fortran::lower::CharacterExprHelper::isCharacter(mlir::Type type) {
return true;
if (auto refType = type.dyn_cast<fir::ReferenceType>())
type = refType.getEleTy();
if (auto seqType = type.dyn_cast<fir::SequenceType>()) {
type = seqType.getEleTy();
}
if (auto seqType = type.dyn_cast<fir::SequenceType>())
if (seqType.getShape().size() == 1)
type = seqType.getEleTy();
return type.isa<fir::CharacterType>();
}

File diff suppressed because it is too large Load Diff

View File

@ -9,11 +9,13 @@
#include "flang/Lower/Mangler.h"
#include "flang/Common/reference.h"
#include "flang/Lower/Utils.h"
#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Optimizer/Support/InternalNames.h"
#include "flang/Semantics/tools.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
// recursively build the vector of module scopes
@ -118,3 +120,49 @@ std::string Fortran::lower::mangle::demangleName(llvm::StringRef name) {
auto result = fir::NameUniquer::deconstruct(name);
return result.second.name;
}
//===----------------------------------------------------------------------===//
// Intrinsic Procedure Mangling
//===----------------------------------------------------------------------===//
/// Helper to encode type into string for intrinsic procedure names.
/// Note: mlir has Type::dump(ostream) methods but it may add "!" that is not
/// suitable for function names.
static std::string typeToString(mlir::Type t) {
if (auto refT{t.dyn_cast<fir::ReferenceType>()})
return "ref_" + typeToString(refT.getEleTy());
if (auto i{t.dyn_cast<mlir::IntegerType>()}) {
return "i" + std::to_string(i.getWidth());
}
if (auto cplx{t.dyn_cast<fir::CplxType>()}) {
return "z" + std::to_string(cplx.getFKind());
}
if (auto real{t.dyn_cast<fir::RealType>()}) {
return "r" + std::to_string(real.getFKind());
}
if (auto f{t.dyn_cast<mlir::FloatType>()}) {
return "f" + std::to_string(f.getWidth());
}
if (auto logical{t.dyn_cast<fir::LogicalType>()}) {
return "l" + std::to_string(logical.getFKind());
}
if (auto character{t.dyn_cast<fir::CharacterType>()}) {
return "c" + std::to_string(character.getFKind());
}
if (auto boxCharacter{t.dyn_cast<fir::BoxCharType>()}) {
return "bc" + std::to_string(boxCharacter.getEleTy().getFKind());
}
llvm_unreachable("no mangling for type");
}
std::string fir::mangleIntrinsicProcedure(llvm::StringRef intrinsic,
mlir::FunctionType funTy) {
std::string name = "fir.";
name.append(intrinsic.str()).append(".");
assert(funTy.getNumResults() == 1 && "only function mangling supported");
name.append(typeToString(funTy.getResult(0)));
auto e = funTy.getNumInputs();
for (decltype(e) i = 0; i < e; ++i)
name.append(".").append(typeToString(funTy.getInput(i)));
return name;
}

View File

@ -178,8 +178,10 @@ SequenceType parseSequence(mlir::DialectAsmParser &parser, mlir::Location) {
return SequenceType::get(shape, eleTy, map);
}
static bool verifyIntegerType(mlir::Type ty) {
return ty.isa<mlir::IntegerType>() || ty.isa<IntType>();
/// Is `ty` a standard or FIR integer type?
static bool isaIntegerType(mlir::Type ty) {
// TODO: why aren't we using isa_integer? investigatation required.
return ty.isa<mlir::IntegerType>() || ty.isa<fir::IntType>();
}
bool verifyRecordMemberType(mlir::Type ty) {
@ -205,7 +207,7 @@ RecordType verifyDerived(mlir::DialectAsmParser &parser, RecordType derivedTy,
return {};
}
for (auto &p : lenPList)
if (!verifyIntegerType(p.second)) {
if (!isaIntegerType(p.second)) {
parser.emitError(loc, "LEN parameter must be integral type");
return {};
}
@ -384,24 +386,22 @@ struct DimsTypeStorage : public mlir::TypeStorage {
static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); }
bool operator==(const KeyTy &key) const {
return key == static_cast<unsigned>(getRank());
}
bool operator==(const KeyTy &key) const { return key == getRank(); }
static DimsTypeStorage *construct(mlir::TypeStorageAllocator &allocator,
int rank) {
unsigned rank) {
auto *storage = allocator.allocate<DimsTypeStorage>();
return new (storage) DimsTypeStorage{rank};
}
int getRank() const { return rank; }
unsigned getRank() const { return rank; }
protected:
int rank;
unsigned rank;
private:
DimsTypeStorage() = delete;
explicit DimsTypeStorage(int rank) : rank{rank} {}
explicit DimsTypeStorage(unsigned rank) : rank{rank} {}
};
/// The type of a derived type part reference
@ -832,6 +832,9 @@ bool isa_std_type(mlir::Type t) {
}
bool isa_fir_or_std_type(mlir::Type t) {
if (auto funcType = t.dyn_cast<mlir::FunctionType>())
return llvm::all_of(funcType.getInputs(), isa_fir_or_std_type) &&
llvm::all_of(funcType.getResults(), isa_fir_or_std_type);
return isa_fir_type(t) || isa_std_type(t);
}
@ -874,7 +877,7 @@ DimsType fir::DimsType::get(mlir::MLIRContext *ctxt, unsigned rank) {
return Base::get(ctxt, FIR_DIMS, rank);
}
int fir::DimsType::getRank() const { return getImpl()->getRank(); }
unsigned fir::DimsType::getRank() const { return getImpl()->getRank(); }
// Field
@ -999,10 +1002,7 @@ fir::ReferenceType::verifyConstructionInvariants(mlir::Location loc,
// Pointer<T>
PointerType fir::PointerType::get(mlir::Type elementType) {
if (!singleIndirectionLevel(elementType)) {
llvm_unreachable("FIXME: invalid element type");
return {};
}
assert(singleIndirectionLevel(elementType) && "invalid element type");
return Base::get(elementType.getContext(), FIR_POINTER, elementType);
}
@ -1030,10 +1030,7 @@ fir::PointerType::verifyConstructionInvariants(mlir::Location loc,
// Heap<T>
HeapType fir::HeapType::get(mlir::Type elementType) {
if (!singleIndirectionLevel(elementType)) {
llvm_unreachable("FIXME: invalid element type");
return {};
}
assert(singleIndirectionLevel(elementType) && "invalid element type");
return Base::get(elementType.getContext(), FIR_HEAP, elementType);
}
@ -1171,7 +1168,6 @@ mlir::Type fir::RecordType::getType(llvm::StringRef ident) {
for (auto f : getTypeList())
if (ident == f.first)
return f.second;
llvm_unreachable("query for field not present in record");
return {};
}
@ -1216,9 +1212,9 @@ llvm::SmallPtrSet<detail::RecordTypeStorage const *, 4> recordTypeVisited;
} // namespace
void fir::verifyIntegralType(mlir::Type type) {
if (verifyIntegerType(type) || type.isa<mlir::IndexType>())
if (isaIntegerType(type) || type.isa<mlir::IndexType>())
return;
llvm_unreachable("expected integral type");
llvm::report_fatal_error("expected integral type");
}
void fir::printFirType(FIROpsDialect *, mlir::Type ty,