Skip to content

Commit

Permalink
[flang] Add CHARACTER type lowering helpers and runtime.
Browse files Browse the repository at this point in the history
In order for these files to build properly, this patch rolls up a number of changes that have been made to various files that have been upstreamed.

Implementations for the interfaces included in Bridge.h and IntrinsicCall.h will be included in a future diff.

Differential revision: https://reviews.llvm.org/D82608
  • Loading branch information
schweitzpgi committed Jun 26, 2020
1 parent ae74252 commit c3477c5
Show file tree
Hide file tree
Showing 16 changed files with 1,123 additions and 86 deletions.
117 changes: 117 additions & 0 deletions flang/include/flang/Lower/Bridge.h
@@ -0,0 +1,117 @@
//===-- Lower/Bridge.h -- main interface to lowering ------------*- 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Implements lowering. Convert Fortran source to
/// [MLIR](https://github.com/tensorflow/mlir).
///
/// [Coding style](https://llvm.org/docs/CodingStandards.html)
///
//===----------------------------------------------------------------------===//

#ifndef FORTRAN_LOWER_BRIDGE_H
#define FORTRAN_LOWER_BRIDGE_H

#include "flang/Common/Fortran.h"
#include "flang/Lower/AbstractConverter.h"
#include "flang/Optimizer/Support/KindMapping.h"
#include "mlir/IR/Module.h"

namespace fir {
struct NameUniquer;
}

namespace Fortran {
namespace common {
class IntrinsicTypeDefaultKinds;
} // namespace common
namespace evaluate {
class IntrinsicProcTable;
} // namespace evaluate
namespace parser {
class CookedSource;
struct Program;
} // namespace parser
namespace semantics {
class SemanticsContext;
} // namespace semantics

namespace lower {

//===----------------------------------------------------------------------===//
// Lowering bridge
//===----------------------------------------------------------------------===//

/// The lowering bridge converts the front-end parse trees and semantics
/// checking residual to MLIR (FIR dialect) code.
class LoweringBridge {
public:
/// Create a lowering bridge instance.
static LoweringBridge
create(const Fortran::common::IntrinsicTypeDefaultKinds &defaultKinds,
const Fortran::evaluate::IntrinsicProcTable &intrinsics,
const Fortran::parser::CookedSource &cooked) {
return LoweringBridge{defaultKinds, intrinsics, cooked};
}

//===--------------------------------------------------------------------===//
// Getters
//===--------------------------------------------------------------------===//

mlir::MLIRContext &getMLIRContext() { return *context.get(); }
mlir::ModuleOp &getModule() { return *module.get(); }
const Fortran::common::IntrinsicTypeDefaultKinds &getDefaultKinds() const {
return defaultKinds;
}
const Fortran::evaluate::IntrinsicProcTable &getIntrinsicTable() const {
return intrinsics;
}
const Fortran::parser::CookedSource *getCookedSource() const {
return cooked;
}

/// Get the kind map.
const fir::KindMapping &getKindMap() const { return kindMap; }

/// Create a folding context. Careful: this is very expensive.
Fortran::evaluate::FoldingContext createFoldingContext() const;

bool validModule() { return getModule(); }

//===--------------------------------------------------------------------===//
// Perform the creation of an mlir::ModuleOp
//===--------------------------------------------------------------------===//

/// Read in an MLIR input file rather than lowering Fortran sources.
/// This is intended to be used for testing.
void parseSourceFile(llvm::SourceMgr &);

/// Cross the bridge from the Fortran parse-tree, etc. to MLIR dialects
void lower(const Fortran::parser::Program &program, fir::NameUniquer &uniquer,
const Fortran::semantics::SemanticsContext &semanticsContext);

private:
explicit LoweringBridge(
const Fortran::common::IntrinsicTypeDefaultKinds &defaultKinds,
const Fortran::evaluate::IntrinsicProcTable &intrinsics,
const Fortran::parser::CookedSource &cooked);
LoweringBridge() = delete;
LoweringBridge(const LoweringBridge &) = delete;

const Fortran::common::IntrinsicTypeDefaultKinds &defaultKinds;
const Fortran::evaluate::IntrinsicProcTable &intrinsics;
const Fortran::parser::CookedSource *cooked;
std::unique_ptr<mlir::MLIRContext> context;
std::unique_ptr<mlir::ModuleOp> module;
fir::KindMapping kindMap;
};

} // namespace lower
} // namespace Fortran

#endif // FORTRAN_LOWER_BRIDGE_H
140 changes: 140 additions & 0 deletions flang/include/flang/Lower/CharacterExpr.h
@@ -0,0 +1,140 @@
//===-- Lower/CharacterExpr.h -- lowering of characters ---------*- 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
//
//===----------------------------------------------------------------------===//

#ifndef FORTRAN_LOWER_CHARACTEREXPR_H
#define FORTRAN_LOWER_CHARACTEREXPR_H

#include "flang/Lower/FIRBuilder.h"
#include "flang/Lower/Support/BoxValue.h"

namespace Fortran::lower {

/// Helper to facilitate lowering of CHARACTER in FIR.
class CharacterExprHelper {
public:
/// Constructor.
explicit CharacterExprHelper(FirOpBuilder &builder, mlir::Location loc)
: builder{builder}, loc{loc} {}
CharacterExprHelper(const CharacterExprHelper &) = delete;

/// Unless otherwise stated, all mlir::Value inputs of these pseudo-fir ops
/// must be of type:
/// - fir.boxchar<kind> (dynamic length character),
/// - fir.ref<fir.array<len x fir.char<kind>>> (character with compile time
/// constant length),
/// - fir.array<len x fir.char<kind>> (compile time constant character)

/// Copy the \p count first characters of \p src into \p dest.
/// \p count can have any integer type.
void createCopy(mlir::Value dest, mlir::Value src, mlir::Value count);

/// Set characters of \p str at position [\p lower, \p upper) to blanks.
/// \p lower and \upper bounds are zero based.
/// If \p upper <= \p lower, no padding is done.
/// \p upper and \p lower can have any integer type.
void createPadding(mlir::Value str, mlir::Value lower, mlir::Value upper);

/// Create str(lb:ub), lower bounds must always be specified, upper
/// bound is optional.
mlir::Value createSubstring(mlir::Value str,
llvm::ArrayRef<mlir::Value> bounds);

/// Return blank character of given \p type !fir.char<kind>
mlir::Value createBlankConstant(fir::CharacterType type);

/// Lower \p lhs = \p rhs where \p lhs and \p rhs are scalar characters.
/// It handles cases where \p lhs and \p rhs may overlap.
void createAssign(mlir::Value lhs, mlir::Value rhs);

/// Lower an assignment where the buffer and LEN parameter are known and do
/// not need to be unboxed.
void createAssign(mlir::Value lptr, mlir::Value llen, mlir::Value rptr,
mlir::Value rlen);

/// Create lhs // rhs in temp obtained with fir.alloca
mlir::Value createConcatenate(mlir::Value lhs, mlir::Value rhs);

/// LEN_TRIM intrinsic.
mlir::Value createLenTrim(mlir::Value str);

/// Embox \p addr and \p len and return fir.boxchar.
/// Take care of type conversions before emboxing.
/// \p len is converted to the integer type for character lengths if needed.
mlir::Value createEmboxChar(mlir::Value addr, mlir::Value len);

/// Unbox \p boxchar into (fir.ref<fir.char<kind>>, getLengthType()).
std::pair<mlir::Value, mlir::Value> createUnboxChar(mlir::Value boxChar);

/// Allocate a temp of fir::CharacterType type and length len.
/// Returns related fir.ref<fir.char<kind>>.
mlir::Value createCharacterTemp(mlir::Type type, mlir::Value len);

/// Allocate a temp of compile time constant length.
/// Returns related fir.ref<fir.array<len x fir.char<kind>>>.
mlir::Value createCharacterTemp(mlir::Type type, int len) {
return createTemp(type, len);
}

/// Return buffer/length pair of character str, if str is a constant,
/// it is allocated into a temp, otherwise, its memory reference is
/// returned as the buffer.
/// The buffer type of str is of type:
/// - fir.ref<fir.array<len x fir.char<kind>>> if str has compile time
/// constant length.
/// - fir.ref<fir.char<kind>> if str has dynamic length.
std::pair<mlir::Value, mlir::Value> materializeCharacter(mlir::Value str);

/// Return true if \p type is a character literal type (is
/// fir.array<len x fir.char<kind>>).;
static bool isCharacterLiteral(mlir::Type type);

/// Return true if \p type is one of the following type
/// - fir.boxchar<kind>
/// - fir.ref<fir.array<len x fir.char<kind>>>
/// - fir.array<len x fir.char<kind>>
static bool isCharacter(mlir::Type type);

/// Extract the kind of a character type
static int getCharacterKind(mlir::Type type);

/// Return the integer type that must be used to manipulate
/// Character lengths. TODO: move this to FirOpBuilder?
mlir::Type getLengthType() { return builder.getIndexType(); }

private:
fir::CharBoxValue materializeValue(const fir::CharBoxValue &str);
fir::CharBoxValue toDataLengthPair(mlir::Value character);
mlir::Type getReferenceType(const fir::CharBoxValue &c) const;
mlir::Value createEmbox(const fir::CharBoxValue &str);
mlir::Value createLoadCharAt(const fir::CharBoxValue &str, mlir::Value index);
void createStoreCharAt(const fir::CharBoxValue &str, mlir::Value index,
mlir::Value c);
void createCopy(const fir::CharBoxValue &dest, const fir::CharBoxValue &src,
mlir::Value count);
void createPadding(const fir::CharBoxValue &str, mlir::Value lower,
mlir::Value upper);
fir::CharBoxValue createTemp(mlir::Type type, mlir::Value len);
void createLengthOneAssign(const fir::CharBoxValue &lhs,
const fir::CharBoxValue &rhs);
void createAssign(const fir::CharBoxValue &lhs, const fir::CharBoxValue &rhs);
fir::CharBoxValue createConcatenate(const fir::CharBoxValue &lhs,
const fir::CharBoxValue &rhs);
fir::CharBoxValue createSubstring(const fir::CharBoxValue &str,
llvm::ArrayRef<mlir::Value> bounds);
mlir::Value createLenTrim(const fir::CharBoxValue &str);
mlir::Value createTemp(mlir::Type type, int len);
mlir::Value createBlankConstantCode(fir::CharacterType type);

private:
FirOpBuilder &builder;
mlir::Location loc;
};

} // namespace Fortran::lower

#endif // FORTRAN_LOWER_CHARACTEREXPR_H
36 changes: 36 additions & 0 deletions flang/include/flang/Lower/CharacterRuntime.h
@@ -0,0 +1,36 @@
//===-- Lower/CharacterRuntime.h -- lower CHARACTER operations --*- 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
//
//===----------------------------------------------------------------------===//

#ifndef FORTRAN_LOWER_CHARACTERRUNTIME_H
#define FORTRAN_LOWER_CHARACTERRUNTIME_H

#include "mlir/Dialect/StandardOps/IR/Ops.h"

namespace Fortran {
namespace lower {
class AbstractConverter;

/// Generate call to a character comparison for two ssa-values of type
/// `boxchar`.
mlir::Value genBoxCharCompare(AbstractConverter &converter, mlir::Location loc,
mlir::CmpIPredicate cmp, mlir::Value lhs,
mlir::Value rhs);

/// Generate call to a character comparison op for two unboxed variables. There
/// are 4 arguments, 2 for the lhs and 2 for the rhs. Each CHARACTER must pass a
/// reference to its buffer (`ref<char<K>>`) and its LEN type parameter (some
/// integral type).
mlir::Value genRawCharCompare(AbstractConverter &converter, mlir::Location loc,
mlir::CmpIPredicate cmp, mlir::Value lhsBuff,
mlir::Value lhsLen, mlir::Value rhsBuff,
mlir::Value rhsLen);

} // namespace lower
} // namespace Fortran

#endif // FORTRAN_LOWER_CHARACTERRUNTIME_H
4 changes: 4 additions & 0 deletions flang/include/flang/Lower/FIRBuilder.h
Expand Up @@ -70,6 +70,10 @@ class FirOpBuilder : public mlir::OpBuilder {
/// Safely create a reference type to the type `eleTy`.
mlir::Type getRefType(mlir::Type eleTy);

/// Create a null constant of type RefType and value 0. Need to pass in the
/// Location information.
mlir::Value createNullConstant(mlir::Location loc);

/// Create an integer constant of type \p type and value \p i.
mlir::Value createIntegerConstant(mlir::Location loc, mlir::Type integerType,
std::int64_t i);
Expand Down
66 changes: 66 additions & 0 deletions flang/include/flang/Lower/IntrinsicCall.h
@@ -0,0 +1,66 @@
//===-- Lower/IntrinsicCall.h -- lowering of intrinsics ---------*- 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
//
//===----------------------------------------------------------------------===//

#ifndef FORTRAN_LOWER_INTRINSICCALL_H
#define FORTRAN_LOWER_INTRINSICCALL_H

#include "flang/Lower/FIRBuilder.h"

namespace fir {
class ExtendedValue;
}

namespace Fortran::lower {

// TODO: Expose interface to get specific intrinsic function address.
// TODO: Handle intrinsic subroutine.
// TODO: Intrinsics that do not require their arguments to be defined
// (e.g shape inquiries) might not fit in the current interface that
// requires mlir::Value to be provided.
// TODO: Error handling interface ?
// TODO: Implementation is incomplete. Many intrinsics to tbd.

/// 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);

//===--------------------------------------------------------------------===//
// Direct access to intrinsics that may be used by lowering outside
// of intrinsic call lowering.
//===--------------------------------------------------------------------===//

/// 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);

/// Generate minimum. Same constraints as genMax.
mlir::Value genMin(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);

private:
FirOpBuilder &builder;
mlir::Location loc;
};

} // namespace Fortran::lower

#endif // FORTRAN_LOWER_INTRINSICCALL_H

0 comments on commit c3477c5

Please sign in to comment.