From f53a6e96020b531df5e33b3624c225f7cea63397 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Thu, 27 Feb 2020 06:11:09 -0800 Subject: [PATCH] Add FIR --- CMakeLists.txt | 7 + include/fir/.clang-format | 2 - include/flang/CMakeLists.txt | 11 +- include/flang/Optimizer/CMakeLists.txt | 1 + .../flang/Optimizer/Dialect/CMakeLists.txt | 4 + include/flang/Optimizer/Dialect/FIRAttr.h | 166 + include/flang/Optimizer/Dialect/FIRDialect.h | 86 + include/flang/Optimizer/Dialect/FIROps.h | 74 + include/flang/Optimizer/Dialect/FIROps.td | 2779 +++++++++++++++++ .../flang/Optimizer/Dialect/FIROpsSupport.h | 73 + include/flang/Optimizer/Dialect/FIRType.h | 357 +++ include/flang/Optimizer/Support/KindMapping.h | 89 + lib/CMakeLists.txt | 4 + lib/Fir/.clang-format | 2 - lib/Optimizer/CMakeLists.txt | 5 + lib/Optimizer/Dialect/CMakeLists.txt | 27 + lib/Optimizer/Dialect/FIRAttr.cpp | 236 ++ lib/Optimizer/Dialect/FIRDialect.cpp | 77 + lib/Optimizer/Dialect/FIROps.cpp | 573 ++++ lib/Optimizer/Dialect/FIRType.cpp | 1288 ++++++++ lib/Optimizer/Support/CMakeLists.txt | 10 + lib/Optimizer/Support/KindMapping.cpp | 239 ++ test-lit/CMakeLists.txt | 5 + test-lit/Fir/fir-ops.fir | 458 +++ test-lit/Fir/fir-types.fir | 70 + test-lit/lit.cfg.py | 8 +- test-lit/lit.site.cfg.py.in | 1 + tools/CMakeLists.txt | 4 +- tools/tco/CMakeLists.txt | 22 + tools/tco/tco.cpp | 118 + 30 files changed, 6782 insertions(+), 14 deletions(-) delete mode 100644 include/fir/.clang-format create mode 100644 include/flang/Optimizer/CMakeLists.txt create mode 100644 include/flang/Optimizer/Dialect/CMakeLists.txt create mode 100644 include/flang/Optimizer/Dialect/FIRAttr.h create mode 100644 include/flang/Optimizer/Dialect/FIRDialect.h create mode 100644 include/flang/Optimizer/Dialect/FIROps.h create mode 100644 include/flang/Optimizer/Dialect/FIROps.td create mode 100644 include/flang/Optimizer/Dialect/FIROpsSupport.h create mode 100644 include/flang/Optimizer/Dialect/FIRType.h create mode 100644 include/flang/Optimizer/Support/KindMapping.h delete mode 100644 lib/Fir/.clang-format create mode 100644 lib/Optimizer/CMakeLists.txt create mode 100644 lib/Optimizer/Dialect/CMakeLists.txt create mode 100644 lib/Optimizer/Dialect/FIRAttr.cpp create mode 100644 lib/Optimizer/Dialect/FIRDialect.cpp create mode 100644 lib/Optimizer/Dialect/FIROps.cpp create mode 100644 lib/Optimizer/Dialect/FIRType.cpp create mode 100644 lib/Optimizer/Support/CMakeLists.txt create mode 100644 lib/Optimizer/Support/KindMapping.cpp create mode 100644 test-lit/Fir/fir-ops.fir create mode 100644 test-lit/Fir/fir-types.fir create mode 100644 tools/tco/CMakeLists.txt create mode 100644 tools/tco/tco.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b7a823f7ec3..72e66259b1e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,6 +84,13 @@ add_definitions(${LLVM_DEFINITIONS}) set(LLVM_EXTERNAL_LIT ${LLVM_TOOLS_BINARY_DIR}/llvm-lit CACHE STRING "Command used to spawn lit") if(LINK_WITH_FIR) + include(TableGen) + include(AddMLIR) + find_program(MLIR_TABLEGEN_EXE "mlir-tblgen" ${LLVM_TOOLS_BINARY_DIR} + NO_DEFAULT_PATH) + # tco/bbc tools output directory + set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/bin) + set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/lib) message(STATUS "Linking driver with FIR and LLVM") llvm_map_components_to_libnames(LLVM_COMMON_LIBS support) message(STATUS "LLVM libraries: ${LLVM_COMMON_LIBS}") diff --git a/include/fir/.clang-format b/include/fir/.clang-format deleted file mode 100644 index a74fda4b6734..000000000000 --- a/include/fir/.clang-format +++ /dev/null @@ -1,2 +0,0 @@ -BasedOnStyle: LLVM -AlwaysBreakTemplateDeclarations: Yes diff --git a/include/flang/CMakeLists.txt b/include/flang/CMakeLists.txt index 7fae707bdc46..db5d26a83b56 100644 --- a/include/flang/CMakeLists.txt +++ b/include/flang/CMakeLists.txt @@ -1,8 +1,3 @@ -#===-- include/flang/CMakeLists.txt ----------------------------------------===# -# -# 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 -# -#===------------------------------------------------------------------------===# - +if(LINK_WITH_FIR) + add_subdirectory(Optimizer) +endif() diff --git a/include/flang/Optimizer/CMakeLists.txt b/include/flang/Optimizer/CMakeLists.txt new file mode 100644 index 000000000000..0ca0f41c5af4 --- /dev/null +++ b/include/flang/Optimizer/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Dialect) diff --git a/include/flang/Optimizer/Dialect/CMakeLists.txt b/include/flang/Optimizer/Dialect/CMakeLists.txt new file mode 100644 index 000000000000..9528b1abffb0 --- /dev/null +++ b/include/flang/Optimizer/Dialect/CMakeLists.txt @@ -0,0 +1,4 @@ +set(LLVM_TARGET_DEFINITIONS FIROps.td) +mlir_tablegen(FIROps.h.inc -gen-op-decls) +mlir_tablegen(FIROps.cpp.inc -gen-op-defs) +add_public_tablegen_target(FIROpsIncGen) diff --git a/include/flang/Optimizer/Dialect/FIRAttr.h b/include/flang/Optimizer/Dialect/FIRAttr.h new file mode 100644 index 000000000000..3ce9a54a629d --- /dev/null +++ b/include/flang/Optimizer/Dialect/FIRAttr.h @@ -0,0 +1,166 @@ +//===-- optimizer/Dialect/FIRAttr.h -- FIR attributes -----------*- 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 OPTIMIZER_DIALECT_FIRATTR_H +#define OPTIMIZER_DIALECT_FIRATTR_H + +#include "mlir/IR/Attributes.h" + +namespace mlir { +class DialectAsmParser; +class DialectAsmPrinter; +} // namespace mlir + +namespace fir { + +class FIROpsDialect; + +namespace detail { +struct RealAttributeStorage; +struct TypeAttributeStorage; +} // namespace detail + +enum AttributeKind { + FIR_ATTR = mlir::Attribute::FIRST_FIR_ATTR, + FIR_EXACTTYPE, // instance_of, precise type relation + FIR_SUBCLASS, // subsumed_by, is-a (subclass) relation + FIR_POINT, + FIR_CLOSEDCLOSED_INTERVAL, + FIR_OPENCLOSED_INTERVAL, + FIR_CLOSEDOPEN_INTERVAL, + FIR_REAL_ATTR, +}; + +class ExactTypeAttr + : public mlir::Attribute::AttrBase { +public: + using Base::Base; + using ValueType = mlir::Type; + + static constexpr llvm::StringRef getAttrName() { return "instance"; } + static ExactTypeAttr get(mlir::Type value); + + mlir::Type getType() const; + + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { return AttributeKind::FIR_EXACTTYPE; } +}; + +class SubclassAttr + : public mlir::Attribute::AttrBase { +public: + using Base::Base; + using ValueType = mlir::Type; + + static constexpr llvm::StringRef getAttrName() { return "subsumed"; } + static SubclassAttr get(mlir::Type value); + + mlir::Type getType() const; + + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { return AttributeKind::FIR_SUBCLASS; } +}; + +// Attributes for building SELECT CASE multiway branches + +/// A closed interval (including the bound values) is an interval with both an +/// upper and lower bound as given as ssa-values. +/// A case selector of `CASE (n:m)` corresponds to any value from `n` to `m` and +/// is encoded as `#fir.interval, %n, %m`. +class ClosedIntervalAttr + : public mlir::Attribute::AttrBase { +public: + using Base::Base; + + static constexpr llvm::StringRef getAttrName() { return "interval"; } + static ClosedIntervalAttr get(mlir::MLIRContext *ctxt); + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { + return AttributeKind::FIR_CLOSEDCLOSED_INTERVAL; + } +}; + +/// An upper bound is an open interval (including the bound value) as given as +/// an ssa-value. +/// A case selector of `CASE (:m)` corresponds to any value up to and including +/// `m` and is encoded as `#fir.upper, %m`. +class UpperBoundAttr : public mlir::Attribute::AttrBase { +public: + using Base::Base; + + static constexpr llvm::StringRef getAttrName() { return "upper"; } + static UpperBoundAttr get(mlir::MLIRContext *ctxt); + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { + return AttributeKind::FIR_OPENCLOSED_INTERVAL; + } +}; + +/// A lower bound is an open interval (including the bound value) as given as +/// an ssa-value. +/// A case selector of `CASE (n:)` corresponds to any value down to and +/// including `n` and is encoded as `#fir.lower, %n`. +class LowerBoundAttr : public mlir::Attribute::AttrBase { +public: + using Base::Base; + + static constexpr llvm::StringRef getAttrName() { return "lower"; } + static LowerBoundAttr get(mlir::MLIRContext *ctxt); + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { + return AttributeKind::FIR_CLOSEDOPEN_INTERVAL; + } +}; + +/// A pointer interval is an closed interval as given as an ssa-value. The +/// interval contains exactly one value. +/// A case selector of `CASE (p)` corresponds to exactly the value `p` and is +/// encoded as `#fir.point, %p`. +class PointIntervalAttr : public mlir::Attribute::AttrBase { +public: + using Base::Base; + + static constexpr llvm::StringRef getAttrName() { return "point"; } + static PointIntervalAttr get(mlir::MLIRContext *ctxt); + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { return AttributeKind::FIR_POINT; } +}; + +/// A real attribute is used to workaround MLIR's default parsing of a real +/// constant. +/// `#fir.real<10, 3.14>` is used to introduce a real constant of value `3.14` +/// with a kind of `10`. +class RealAttr + : public mlir::Attribute::AttrBase { +public: + using Base::Base; + using ValueType = std::pair; + + static constexpr llvm::StringRef getAttrName() { return "real"; } + static RealAttr get(mlir::MLIRContext *ctxt, const ValueType &key); + + int getFKind() const; + llvm::APFloat getValue() const; + + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { return AttributeKind::FIR_REAL_ATTR; } +}; + +mlir::Attribute parseFirAttribute(FIROpsDialect *dialect, + mlir::DialectAsmParser &parser, + mlir::Type type); + +void printFirAttribute(FIROpsDialect *dialect, mlir::Attribute attr, + mlir::DialectAsmPrinter &p); + +} // namespace fir + +#endif // OPTIMIZER_DIALECT_FIRATTR_H diff --git a/include/flang/Optimizer/Dialect/FIRDialect.h b/include/flang/Optimizer/Dialect/FIRDialect.h new file mode 100644 index 000000000000..0c80bbe80e70 --- /dev/null +++ b/include/flang/Optimizer/Dialect/FIRDialect.h @@ -0,0 +1,86 @@ +//===-- optimizer/Dialect/FIRDialect.h -- FIR dialect -----------*- 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 OPTIMIZER_DIALECT_FIRDIALECT_H +#define OPTIMIZER_DIALECT_FIRDIALECT_H + +#include "mlir/IR/Dialect.h" +#include "mlir/InitAllPasses.h" + +namespace llvm { +class raw_ostream; +class StringRef; +} // namespace llvm + +namespace mlir { +class Attribute; +class DialectAsmParser; +class DialectAsmPrinter; +class Location; +class MLIRContext; +class Type; +} // namespace mlir + +namespace fir { + +/// FIR dialect +class FIROpsDialect final : public mlir::Dialect { +public: + explicit FIROpsDialect(mlir::MLIRContext *ctx); + virtual ~FIROpsDialect(); + + static llvm::StringRef getDialectNamespace() { return "fir"; } + + mlir::Type parseType(mlir::DialectAsmParser &parser) const override; + void printType(mlir::Type ty, mlir::DialectAsmPrinter &p) const override; + + mlir::Attribute parseAttribute(mlir::DialectAsmParser &parser, + mlir::Type type) const override; + void printAttribute(mlir::Attribute attr, + mlir::DialectAsmPrinter &p) const override; +}; + +/// Register the dialect with MLIR +inline void registerFIR() { + // we want to register exactly once + [[maybe_unused]] static bool init_once = [] { + mlir::registerDialect(); + return true; + }(); +} + +/// Register the standard passes we use. This comes from registerAllPasses(), +/// but is a smaller set since we aren't using many of the passes found there. +inline void registerGeneralPasses() { + mlir::createCanonicalizerPass(); + mlir::createCSEPass(); + mlir::createVectorizePass({}); + mlir::createLoopUnrollPass(); + mlir::createLoopUnrollAndJamPass(); + mlir::createSimplifyAffineStructuresPass(); + mlir::createLoopFusionPass(); + mlir::createLoopInvariantCodeMotionPass(); + mlir::createAffineLoopInvariantCodeMotionPass(); + mlir::createPipelineDataTransferPass(); + mlir::createLowerAffinePass(); + mlir::createLoopTilingPass(0); + mlir::createLoopCoalescingPass(); + mlir::createAffineDataCopyGenerationPass(0, 0); + mlir::createMemRefDataFlowOptPass(); + mlir::createStripDebugInfoPass(); + mlir::createPrintOpStatsPass(); + mlir::createInlinerPass(); + mlir::createSymbolDCEPass(); + mlir::createLocationSnapshotPass({}); +} + +inline void registerFIRPasses() { registerGeneralPasses(); } + +} // namespace fir + +#endif // OPTIMIZER_DIALECT_FIRDIALECT_H diff --git a/include/flang/Optimizer/Dialect/FIROps.h b/include/flang/Optimizer/Dialect/FIROps.h new file mode 100644 index 000000000000..9ef84975796a --- /dev/null +++ b/include/flang/Optimizer/Dialect/FIROps.h @@ -0,0 +1,74 @@ +//===-- optimizer/Dialect/FIROps.h - FIR 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 OPTIMIZER_DIALECT_FIROPS_H +#define OPTIMIZER_DIALECT_FIROPS_H + +#include "mlir/IR/Builders.h" +#include "mlir/IR/OpDefinition.h" +#include "mlir/IR/OpImplementation.h" +#include "mlir/IR/SymbolTable.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +using namespace mlir; + +namespace fir { + +class FirEndOp; + +enum class CmpFPredicate { + FirstValidValue, + // Always false + AlwaysFalse = FirstValidValue, + // Ordered comparisons + OEQ, + OGT, + OGE, + OLT, + OLE, + ONE, + // Both ordered + ORD, + // Unordered comparisons + UEQ, + UGT, + UGE, + ULT, + ULE, + UNE, + // Any unordered + UNO, + // Always true + AlwaysTrue, + // Number of predicates. + NumPredicates +}; + +ParseResult isValidCaseAttr(Attribute attr); +unsigned getCaseArgumentOffset(ArrayRef cases, unsigned dest); +ParseResult parseSelector(OpAsmParser *parser, OperationState *result, + OpAsmParser::OperandType &selector, mlir::Type &type); + +void buildCmpFOp(Builder *builder, OperationState &result, + CmpFPredicate predicate, Value lhs, Value rhs); +void buildCmpCOp(Builder *builder, OperationState &result, + CmpFPredicate predicate, Value lhs, Value rhs); +ParseResult parseCmpfOp(OpAsmParser &parser, OperationState &result); +ParseResult parseCmpcOp(OpAsmParser &parser, OperationState &result); + +#define GET_OP_CLASSES +#include "flang/Optimizer/Dialect/FIROps.h.inc" + +LoopOp getForInductionVarOwner(Value val); + +bool isReferenceLike(mlir::Type type); + +} // namespace fir + +#endif // OPTIMIZER_DIALECT_FIROPS_H diff --git a/include/flang/Optimizer/Dialect/FIROps.td b/include/flang/Optimizer/Dialect/FIROps.td new file mode 100644 index 000000000000..7ce9efd40786 --- /dev/null +++ b/include/flang/Optimizer/Dialect/FIROps.td @@ -0,0 +1,2779 @@ +//===-- FIROps.td - FIR operation definitions --------------*- tablegen -*-===// +// +// 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 +/// Definition of the FIR dialect operations +/// +//===----------------------------------------------------------------------===// + +#ifndef FIR_DIALECT_FIR_OPS +#define FIR_DIALECT_FIR_OPS + +#ifndef OP_BASE +include "mlir/IR/OpBase.td" +#endif + +def fir_Dialect : Dialect { + let name = "fir"; +} + +// Types and predicates + +def fir_Type : Type, + "FIR dialect type">; + +// Fortran intrinsic types +def fir_CharacterType : Type()">, + "FIR character type">; +def fir_ComplexType : Type()">, + "FIR complex type">; +def fir_IntegerType : Type()">, + "FIR integer type">; +def fir_LogicalType : Type()">, + "FIR logical type">; +def fir_RealType : Type()">, + "FIR real type">; + +// Generalized FIR and standard dialect types representing intrinsic types +def AnyIntegerLike : TypeConstraint, "any integer">; +def AnyLogicalLike : TypeConstraint, "any logical">; +def AnyRealLike : TypeConstraint, "any real">; +def AnyIntegerType : Type; + +// Fortran derived (user defined) type +def fir_RecordType : Type()">, + "FIR derived type">; + +// Fortran array attribute +def fir_SequenceType : Type()">, + "array type">; + +// Composable types +def AnyCompositeLike : TypeConstraint, "any composite">; + +// Reference to an entity type +def fir_ReferenceType : Type()">, + "reference type">; + +// Reference to an ALLOCATABLE attribute type +def fir_HeapType : Type()">, + "allocatable type">; + +// Reference to a POINTER attribute type +def fir_PointerType : Type()">, + "pointer type">; + +// Reference types +def AnyReferenceLike : TypeConstraint, "any reference">; + +// A descriptor tuple (captures a reference to an entity and other information) +def fir_BoxType : Type()">, "box type">; + +// CHARACTER type descriptor. A pair of a data reference and a LEN value. +def fir_BoxCharType : Type()">, + "box character type">; + +// PROCEDURE POINTER descriptor. A pair that can capture a host closure. +def fir_BoxProcType : Type()">, + "box procedure type">; + +def AnyBoxLike : TypeConstraint, "any box">; + +def AnyRefOrBox : TypeConstraint, + "any reference or box">; + +// A vector of Fortran triple notation describing a multidimensional array +def fir_DimsType : Type()">, "dim type">; +def AnyEmboxLike : TypeConstraint, + "any legal embox argument type">; +def AnyEmboxArg : Type; + +// A type descriptor's type +def fir_TypeDescType : Type()">, + "type desc type">; + +// A field (in a RecordType) argument's type +def fir_FieldType : Type()">, "field type">; + +// A LEN parameter (in a RecordType) argument's type +def fir_LenType : Type()">, + "LEN parameter type">; + +def AnyComponentLike : TypeConstraint, + "any coordinate index">; +def AnyComponentType : Type; + +def AnyCoordinateLike : TypeConstraint, "any coordinate index">; +def AnyCoordinateType : Type; + +// Base class for FIR operations. +// All operations automatically get a prefix of "fir.". +class fir_Op traits> + : Op; + +// Base class for FIR operations that take a single argument +class fir_SimpleOp traits> + : fir_Op { + + let assemblyFormat = [{ + operands attr-dict `:` functional-type(operands, results) + }]; +} + +// Base builder for allocate operations +def fir_AllocateOpBuilder : OpBuilder< + "Builder *builder, OperationState &result, Type inType," + "ArrayRef lenParams = {}, ArrayRef sizes = {}," + "ArrayRef attributes = {}", + [{ + result.addTypes(getRefTy(inType)); + result.addAttribute("in_type", mlir::TypeAttr::get(inType)); + result.addOperands(sizes); + for (auto namedAttr : attributes) + result.addAttribute(namedAttr.first, namedAttr.second); + }]>; + +def fir_NamedAllocateOpBuilder : OpBuilder< + "Builder *builder, OperationState &result, Type inType, StringRef name," + "ArrayRef lenParams = {}, ArrayRef sizes = {}," + "ArrayRef attributes = {}", + [{ + result.addTypes(getRefTy(inType)); + result.addAttribute("in_type", mlir::TypeAttr::get(inType)); + result.addAttribute("name", builder->getStringAttr(name)); + result.addOperands(sizes); + for (auto namedAttr : attributes) + result.addAttribute(namedAttr.first, namedAttr.second); + }]>; + +def fir_OneResultOpBuilder : OpBuilder< + "Builder *, OperationState &result, Type resultType," + "ArrayRef operands, ArrayRef attributes = {}", + [{ + if (resultType) + result.addTypes(resultType); + result.addOperands(operands); + for (auto namedAttr : attributes) + result.addAttribute(namedAttr.first, namedAttr.second); + }]>; + +// Base class of FIR operations that return 1 result +class fir_OneResultOp traits = []> : + fir_Op, Results<(outs fir_Type:$res)> { + let builders = [fir_OneResultOpBuilder]; +} + +// Base class of FIR operations that have 1 argument and return 1 result +class fir_SimpleOneResultOp traits = []> : + fir_SimpleOp { + let builders = [fir_OneResultOpBuilder]; +} + +class fir_TwoBuilders { + list builders = [b1, b2]; +} + +class fir_AllocatableBaseOp traits = []> : + fir_Op, Results<(outs fir_Type:$res)> { + let arguments = (ins + OptionalAttr:$name, + OptionalAttr:$target + ); +} + +class fir_AllocatableOp traits =[]> : + fir_AllocatableBaseOp, + fir_TwoBuilders, + Arguments<(ins TypeAttr:$in_type, Variadic:$args)> { + + let parser = [{ + mlir::Type intype; + if (parser.parseType(intype)) + return mlir::failure(); + auto &builder = parser.getBuilder(); + result.addAttribute(inType(), mlir::TypeAttr::get(intype)); + llvm::SmallVector operands; + llvm::SmallVector typeVec; + bool hasOperands = false; + if (!parser.parseOptionalLParen()) { + // parse the LEN params of the derived type. ( : ) + if (parser.parseOperandList(operands, + mlir::OpAsmParser::Delimiter::None) || + parser.parseColonTypeList(typeVec) || + parser.parseRParen()) + return mlir::failure(); + auto lens = builder.getI32IntegerAttr(operands.size()); + result.addAttribute(lenpName(), lens); + hasOperands = true; + } + if (!parser.parseOptionalComma()) { + // parse size to scale by, vector of n dimensions of type index + auto opSize = operands.size(); + if (parser.parseOperandList(operands, mlir::OpAsmParser::Delimiter::None)) + return mlir::failure(); + for (auto i = opSize, end = operands.size(); i != end; ++i) + typeVec.push_back(builder.getIndexType()); + hasOperands = true; + } + if (hasOperands && + parser.resolveOperands(operands, typeVec, parser.getNameLoc(), + result.operands)) + return mlir::failure(); + mlir::Type restype = wrapResultType(intype); + if (!restype) { + parser.emitError(parser.getNameLoc(), "invalid allocate type: ") + << intype; + return mlir::failure(); + } + if (parser.parseOptionalAttrDict(result.attributes) || + parser.addTypeToList(restype, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr(inType()); + if (hasLenParams()) { + // print the LEN parameters to a derived type in parens + p << '('; + p.printOperands(getLenParams()); + p << " : "; + mlir::interleaveComma(getLenParams(), p.getStream(), + [&](const auto &opnd) { + p.printType(opnd.getType()); + }); + p << ')'; + } + // print the shape of the allocation (if any); all must be index type + for (auto sh : getShapeOperands()) { + p << ", "; + p.printOperand(sh); + } + p.printOptionalAttrDict(getAttrs(), {inType(), lenpName()}); + }]; + + string extraAllocClassDeclaration = [{ + static constexpr llvm::StringRef inType() { return "in_type"; } + static constexpr llvm::StringRef lenpName() { return "len_param_count"; } + mlir::Type getAllocatedType(); + bool hasLenParams() { return bool{getAttr(lenpName())}; } + unsigned numLenParams() { + if (auto val = getAttrOfType(lenpName())) + return val.getInt(); + return 0; + } + operand_range getLenParams() { + return {operand_begin(), operand_begin() + numLenParams()}; + } + operand_range getShapeOperands() { + return {operand_begin() + numLenParams(), operand_end()}; + } + static mlir::Type getRefTy(mlir::Type ty); + + /// Get the input type of the allocation + mlir::Type getInType() { + return getAttrOfType(inType()).getValue(); + } + }]; + + // Verify checks common to all allocation operations + string allocVerify = [{ + llvm::SmallVector visited; + if (verifyInType(getInType(), visited)) + return emitOpError("invalid type for allocation"); + if (verifyRecordLenParams(getInType(), numLenParams())) + return emitOpError("LEN params do not correspond to type"); + }]; +} + +// Memory SSA operations + +def fir_AllocaOp : fir_AllocatableOp<"alloca"> { + let summary = "allocate storage for a temporary on the stack given a type"; + let description = [{ + This primitive operation is used to allocate an object on the stack. A + reference to the object of type `!fir.ref` is returned. The returned + object has an undefined/uninitialized state. The allocation can be given + an optional name. The allocation may have a dynamic repetition count + for allocating a sequence of locations for the specified type. + + %c = ... : i64 + %x = fir.alloca i32 + %y = fir.alloca !fir.array<8 x i64> + %z = fir.alloca f32, %c + + %i = ... : i16 + %j = ... : i32 + %w = fir.alloca !fir.type (%i, %j : i16, i32) + + Note that in the case of `%z`, a contiguous block of memory is allocated + and its size is a runtime multiple of a 32-bit REAL value. + + In the case of `%w`, the arguments `%i` and `%j` are LEN parameters + (`len1`, `len2`) to the type `PT`. + + Finally, the operation is undefined if the ssa-value `%c` is negative. + }]; + + let results = (outs fir_ReferenceType); + + let verifier = allocVerify#[{ + mlir::Type outType = getType(); + if (!outType.isa()) + return emitOpError("must be a !fir.ref type"); + return mlir::success(); + }]; + + let extraClassDeclaration = extraAllocClassDeclaration#[{ + static mlir::Type wrapResultType(mlir::Type intype); + }]; +} + +def fir_LoadOp : fir_OneResultOp<"load", []> { + let summary = "load a value from a memory reference"; + let description = [{ + Load a value from a memory reference into an ssa-value (virtual register). + Produces an immutable ssa-value of the referent type. A memory reference + has type `!fir.ref`, `!fir.heap`, or `!fir.ptr`. + + %a = fir.alloca i32 + %l = fir.load %a : !fir.ref + + The ssa-value has an undefined value if the memory reference is undefined + or null. + }]; + + let arguments = (ins AnyReferenceLike:$memref); + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, Value refVal", + [{ + if (!refVal) { + mlir::emitError(result.location, "LoadOp has null argument"); + return; + } + fir::ReferenceType refTy = refVal.getType().cast(); + result.addOperands(refVal); + result.addTypes(refTy.getEleTy()); + }] + >]; + + let parser = [{ + mlir::Type type; + mlir::OpAsmParser::OperandType oper; + if (parser.parseOperand(oper) || + parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(type) || + parser.resolveOperand(oper, type, result.operands)) + return mlir::failure(); + mlir::Type eleTy; + if (getElementOf(eleTy, type) || + parser.addTypeToList(eleTy, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(memref()); + p.printOptionalAttrDict(getAttrs(), {}); + p << " : " << memref().getType(); + }]; + + let extraClassDeclaration = [{ + static mlir::ParseResult getElementOf(mlir::Type &ele, mlir::Type ref); + }]; +} + +def fir_StoreOp : fir_Op<"store", []> { + let summary = "store an SSA-value to a memory location"; + + let description = [{ + Store an ssa-value (virtual register) to a memory reference. The stored + value must be of the same type as the referent type of the memory + reference. + + %v = ... : f64 + %p = ... : !fir.ptr + fir.store %v to %p : !fir.ptr + + The above store changes the value to which the pointer is pointing and not + the pointer itself. The operation is undefined if the memory reference, + `%p`, is undefined or null. + }]; + + let arguments = (ins AnyType:$value, AnyReferenceLike:$memref); + + let parser = [{ + mlir::Type type; + mlir::OpAsmParser::OperandType oper; + mlir::OpAsmParser::OperandType store; + if (parser.parseOperand(oper) || + parser.parseKeyword("to") || + parser.parseOperand(store) || + parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(type) || + parser.resolveOperand(oper, elementType(type), + result.operands) || + parser.resolveOperand(store, type, result.operands)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(value()); + p << " to "; + p.printOperand(memref()); + p.printOptionalAttrDict(getAttrs(), {}); + p << " : " << memref().getType(); + }]; + + let verifier = [{ + if (value().getType() != fir::dyn_cast_ptrEleTy(memref().getType())) + return emitOpError("store value type must match memory reference type"); + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + static mlir::Type elementType(mlir::Type refType); + }]; +} + +def fir_UndefOp : fir_OneResultOp<"undefined", [NoSideEffect]> { + let summary = "explicit undefined value of some type"; + let description = [{ + Constructs an ssa-value of the specified type with an undefined value. + This operation is typically created internally by the mem2reg conversion + pass. An undefined value can be of any type except `!fir.ref`. + + %a = fir.undefined !fir.array<10 x !fir.type> + + The example creates an array shaped ssa value. The array is rank 1, extent + 10, and each element has type `!fir.type`. + }]; + + let verifier = [{ + if (auto ref = getType().dyn_cast()) + return emitOpError("undefined values of type !fir.ref not allowed"); + return mlir::success(); + }]; + + let parser = [{ + mlir::Type intype; + if (parser.parseType(intype) || + parser.parseOptionalAttrDict(result.attributes) || + parser.addTypeToList(intype, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getType(); + p.printOptionalAttrDict(getAttrs(), {}); + }]; +} + +def fir_AllocMemOp : fir_AllocatableOp<"allocmem"> { + let summary = "allocate storage on the heap for an object of a given type"; + + let description = [{ + Creates a heap memory reference suitable for storing a value of the + given type, T. The heap refernce returned has type `!fir.heap`. + The memory object is in an undefined state. `allocmem` operations must + be paired with `freemem` operations to avoid memory leaks. + + %0 = fir.allocmem !fir.array<10 x f32> + fir.freemem %0 : !fir.heap> + }]; + + let results = (outs fir_HeapType); + + let verifier = allocVerify#[{ + mlir::Type outType = getType(); + if (!outType.dyn_cast()) + return emitOpError("must be a !fir.heap type"); + return mlir::success(); + }]; + + let extraClassDeclaration = extraAllocClassDeclaration#[{ + static mlir::Type wrapResultType(mlir::Type intype); + }]; +} + +def fir_FreeMemOp : fir_Op<"freemem", []> { + let summary = "free a heap object"; + + let description = [{ + Deallocates a heap memory reference that was allocated by an `allocmem`. + The memory object that is deallocated is placed in an undefined state + after `fir.freemem`. Optimizations may treat the loading of an object + in the undefined state as undefined behavior. This includes aliasing + references, such as the result of an `fir.embox`. + + %21 = fir.allocmem !fir.type + ... + fir.freemem %21 : !fir.heap> + }]; + + let arguments = (ins fir_HeapType:$heapref); + + let assemblyFormat = "$heapref attr-dict `:` type($heapref)"; +} + +// Terminator operations + +class fir_SwitchTerminatorOp traits = []> : + fir_Op { + let arguments = (ins Variadic:$args); + + let results = (outs); + + let builders = [OpBuilder< + "Builder *, OperationState &result, Value selector," + "ArrayRef properOperands, ArrayRef destinations," + "ArrayRef> operands = {}," + "ArrayRef attributes = {}", + [{ + result.addOperands(selector); + result.addOperands(properOperands); + for (auto kvp : llvm::zip(destinations, operands)) { + result.addSuccessor(std::get<0>(kvp), std::get<1>(kvp)); + } + for (auto namedAttr : attributes) { + result.addAttribute(namedAttr.first, namedAttr.second); + } + }] + >]; + + string extraSwitchClassDeclaration = [{ + using Conditions = mlir::Value; + static constexpr auto AttrName = "cases"; + + // The number of destination conditions that may be tested + unsigned getNumConditions() { return getNumDest(); } + + // The selector is the value being tested to determine the destination + mlir::Value getSelector() { return getOperand(0); } + + // The number of blocks that may be branched to + unsigned getNumDest() { return getOperation()->getNumSuccessors(); } + }]; +} + +class fir_IntegralSwitchTerminatorOp traits = []> : fir_SwitchTerminatorOp { + let parser = [{ + mlir::OpAsmParser::OperandType selector; + mlir::Type type; + if (parseSelector(parser, result, selector, type)) + return mlir::failure(); + + llvm::SmallVector ivalues; + llvm::SmallVector dests; + llvm::SmallVector, 4> destArgs; + while (true) { + mlir::Attribute ivalue; // Integer or Unit + mlir::Block *dest; + llvm::SmallVector destArg; + llvm::SmallVector temp; + if (parser.parseAttribute(ivalue, "i", temp) || + parser.parseComma() || + parser.parseSuccessorAndUseList(dest, destArg)) + return mlir::failure(); + ivalues.push_back(ivalue); + dests.push_back(dest); + destArgs.push_back(destArg); + if (!parser.parseOptionalRSquare()) + break; + if (parser.parseComma()) + return mlir::failure(); + } + result.addAttribute(AttrName, parser.getBuilder().getArrayAttr(ivalues)); + for (unsigned i = 0, count = dests.size(); i != count; ++i) + result.addSuccessor(dests[i], destArgs[i]); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(getSelector()); + p << " : " << getSelector().getType() << " ["; + auto cases = getAttrOfType(AttrName).getValue(); + for (unsigned i = 0, count = getNumConditions(); i != count; ++i) { + if (i) + p << ", "; + auto &attr = cases[i]; + if (auto intAttr = attr.dyn_cast_or_null()) + p << intAttr.getValue(); + else + p.printAttribute(attr); + p << ", "; + p.printSuccessorAndUseList(getOperation(), i); + } + p << ']'; + p.printOptionalAttrDict(getAttrs(), {AttrName}); + }]; + + let verifier = [{ + if (!(getSelector().getType().isa() || + getSelector().getType().isa() || + getSelector().getType().isa())) + return emitOpError("must be an integer"); + auto cases = getAttrOfType(AttrName).getValue(); + for (unsigned i = 0, count = getNumConditions(); i != count; ++i) { + auto &attr = cases[i]; + if (attr.dyn_cast_or_null()) { + // ok + } else if (attr.dyn_cast_or_null()) { + // ok + } else { + return emitOpError("invalid case alternative"); + } + } + return mlir::success(); + }]; + + let successors = (successor VariadicSuccessor:$targets); + + let extraClassDeclaration = extraSwitchClassDeclaration; +} + +def fir_SelectOp : fir_IntegralSwitchTerminatorOp<"select"> { + let summary = "a multiway branch"; + + let description = [{ + A multiway branch terminator with similar semantics to C's `switch` + statement. A selector value is matched against a list of constants + of the same type for a match. When a match is found, control is + transferred to the corresponding basic block. A `select` must have + at least one basic block with a corresponding `unit` match, and + that block will be selected when all other conditions fail to match. + + fir.select %arg:i32 [1, ^bb1(%0 : i32), + 2, ^bb2(%2,%arg,%arg2 : i32,i32,i32), + -3, ^bb3(%arg2,%2 : i32,i32), + 4, ^bb4(%1 : i32), + unit, ^bb5] + }]; +} + +def fir_SelectRankOp : fir_IntegralSwitchTerminatorOp<"select_rank"> { + let summary = "Fortran's SELECT RANK statement"; + + let description = [{ + Similar to `select`, `select_rank` provides a way to express Fortran's + SELECT RANK construct. In this case, the rank of the selector value + is matched against constants of integer type. The structure is the + same as `select`, but `select_rank` determines the rank of the selector + variable at runtime to determine the best match. + + fir.select_rank %arg:i32 [1, ^bb1(%0 : i32), + 2, ^bb2(%2,%arg,%arg2 : i32,i32,i32), + 3, ^bb3(%arg2,%2 : i32,i32), + -1, ^bb4(%1 : i32), + unit, ^bb5] + }]; +} + +def fir_SelectCaseOp : fir_SwitchTerminatorOp<"select_case"> { + let summary = "Fortran's SELECT CASE statement"; + + let description = [{ + Similar to `select`, `select_case` provides a way to express Fortran's + SELECT CASE construct. In this case, the selector value is matched + against variables (not just constants) and ranges. The structure is + the same as `select`, but `select_case` allows for the expression of + more complex match conditions. + + fir.select_case %arg : i32 [ + #fir.point, %0, ^bb1(%0 : i32), + #fir.lower, %1, ^bb2(%2,%arg,%arg2,%1 : i32,i32,i32,i32), + #fir.interval, %2, %3, ^bb3(%2,%arg2 : i32,i32), + #fir.upper, %arg, ^bb4(%1 : i32), + unit, ^bb5] + }]; + + let parser = [{ + mlir::OpAsmParser::OperandType selector; + mlir::Type type; + if (parseSelector(parser, result, selector, type)) + return mlir::failure(); + + llvm::SmallVector attrs; + llvm::SmallVector opers; + llvm::SmallVector dests; + llvm::SmallVector, 4> destArgs; + while (true) { + mlir::Attribute attr; + mlir::Block *dest; + llvm::SmallVector destArg; + llvm::SmallVector temp; + if (parser.parseAttribute(attr, "a", temp) || + isValidCaseAttr(attr) || + parser.parseComma()) + return mlir::failure(); + attrs.push_back(attr); + if (attr.dyn_cast_or_null()) { + // do nothing + } else if (attr.dyn_cast_or_null()) { + mlir::OpAsmParser::OperandType oper1; + mlir::OpAsmParser::OperandType oper2; + if (parser.parseOperand(oper1) || + parser.parseComma() || + parser.parseOperand(oper2) || + parser.parseComma()) + return mlir::failure(); + opers.push_back(oper1); + opers.push_back(oper2); + } else { + mlir::OpAsmParser::OperandType oper; + if (parser.parseOperand(oper) || + parser.parseComma()) + return mlir::failure(); + opers.push_back(oper); + } + if (parser.parseSuccessorAndUseList(dest, destArg)) + return mlir::failure(); + dests.push_back(dest); + destArgs.push_back(destArg); + if (!parser.parseOptionalRSquare()) + break; + if (parser.parseComma()) + return mlir::failure(); + } + result.addAttribute(AttrName, parser.getBuilder().getArrayAttr(attrs)); + if (parser.resolveOperands(opers, type, result.operands)) + return mlir::failure(); + for (unsigned i = 0, count = dests.size(); i != count; ++i) + result.addSuccessor(dests[i], destArgs[i]); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(getSelector()); + p << " : " << getSelector().getType() << " ["; + auto cases = getAttrOfType(AttrName).getValue(); + for (unsigned i = 0, count = getNumConditions(); i != count; ++i) { + if (i) + p << ", "; + p << cases[i] << ", "; + if (!cases[i].dyn_cast_or_null()) { + p.printOperand(getCaseArg(i, 0)); + p << ", "; + if (cases[i].dyn_cast_or_null()) { + p.printOperand(getCaseArg(i, 1)); + p << ", "; + } + } + p.printSuccessorAndUseList(getOperation(), i); + } + p << ']'; + p.printOptionalAttrDict(getAttrs(), {AttrName}); + }]; + + let verifier = [{ + if (!(getSelector().getType().isa() || + getSelector().getType().isa() || + getSelector().getType().isa() || + getSelector().getType().isa() || + getSelector().getType().isa())) + return emitOpError("must be an integer, character, or logical"); + auto cases = getAttrOfType(AttrName).getValue(); + for (unsigned i = 0, count = getNumConditions(); i != count; ++i) { + auto &attr = cases[i]; + if (attr.isa() || + attr.isa() || + attr.isa() || + attr.isa() || + attr.isa()) { + // ok + } else { + return emitOpError("incorrect select case attribute type"); + } + } + return mlir::success(); + }]; + + let successors = (successor VariadicSuccessor:$targets); + + let extraClassDeclaration = extraSwitchClassDeclaration#[{ + mlir::Value getCaseArg(unsigned dest, unsigned ele) { + assert(ele < 2); + assert(dest < getNumConditions()); + auto cases = getAttrOfType(AttrName).getValue(); + assert(cases.size() == getNumConditions()); + unsigned o = getCaseArgumentOffset(cases, dest); + return getOperand(o + 1 + ele); + } + }]; +} + +def fir_SelectTypeOp : fir_SwitchTerminatorOp<"select_type"> { + let summary = "Fortran's SELECT TYPE statement"; + + let description = [{ + Similar to `select`, `select_type` provides a way to express Fortran's + SELECT TYPE construct. In this case, the type of the selector value + is matched against a list of type descriptors. The structure is the + same as `select`, but `select_type` determines the type of the selector + variable at runtime to determine the best match. + + fir.select_type %arg : !fir.box<()> [ + #fir.instance>, ^bb1(%0 : i32), + #fir.instance>, ^bb2(%2 : i32), + #fir.subsumed>, ^bb3(%2 : i32), + #fir.instance>, ^bb4(%1,%3 : i32,f32), + unit, ^bb5] + }]; + + let parser = [{ + mlir::OpAsmParser::OperandType selector; + mlir::Type type; + if (parseSelector(parser, result, selector, type)) + return mlir::failure(); + + llvm::SmallVector attrs; + llvm::SmallVector dests; + llvm::SmallVector, 4> destArgs; + while (true) { + mlir::Attribute attr; + mlir::Block *dest; + llvm::SmallVector destArg; + llvm::SmallVector temp; + if (parser.parseAttribute(attr, "a", temp) || + parser.parseComma() || + parser.parseSuccessorAndUseList(dest, destArg)) + return mlir::failure(); + attrs.push_back(attr); + dests.push_back(dest); + destArgs.push_back(destArg); + if (!parser.parseOptionalRSquare()) + break; + if (parser.parseComma()) + return mlir::failure(); + } + result.addAttribute(AttrName, parser.getBuilder().getArrayAttr(attrs)); + for (unsigned i = 0, count = dests.size(); i != count; ++i) + result.addSuccessor(dests[i], destArgs[i]); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(getSelector()); + p << " : " << getSelector().getType() << " ["; + auto cases = getAttrOfType(AttrName).getValue(); + for (unsigned i = 0, count = getNumConditions(); i != count; ++i) { + if (i) + p << ", "; + p << cases[i] << ", "; + p.printSuccessorAndUseList(getOperation(), i); + } + p << ']'; + p.printOptionalAttrDict(getAttrs(), {AttrName}); + }]; + + let verifier = [{ + if (!(getSelector().getType().isa())) + return emitOpError("must be a boxed type"); + auto cases = getAttrOfType(AttrName).getValue(); + for (unsigned i = 0, count = getNumConditions(); i != count; ++i) { + auto &attr = cases[i]; + if (attr.dyn_cast_or_null() || + attr.dyn_cast_or_null() || + attr.dyn_cast_or_null()) { + // ok + } else { + return emitOpError("invalid type-case alternative"); + } + } + return mlir::success(); + }]; + + let successors = (successor VariadicSuccessor:$targets); + + let extraClassDeclaration = extraSwitchClassDeclaration; +} + +def fir_UnreachableOp : fir_Op<"unreachable", [Terminator]> { + let summary = "the unreachable instruction"; + + let description = [{ + Terminates a basic block with the assertion that the end of the block + will never be reached at runtime. This instruction can be used + immediately after a call to the Fortran runtime to terminate the + program, for example. This instruction corresponds to the LLVM IR + instruction `unreachable`. + + fir.unreachable + }]; + + let parser = "return mlir::success();"; + + let printer = "p << getOperationName();"; +} + +def fir_FirEndOp : fir_Op<"end", [Terminator]> { + let summary = "the end instruction"; + + let description = [{ + The end terminator is a special terminator used inside various FIR + operations that have regions. End is thus the custom invisible terminator + for these operations. It is implicit and need not appear in the textual + representation. + }]; +} + +def fir_HasValueOp : fir_Op<"has_value", [Terminator, HasParent<"GlobalOp">]> { + let summary = "terminator for GlobalOp"; + let description = [{ + The terminator for a GlobalOp with a body. + + global @variable : tuple { + %0 = constant 45 : i32 + %1 = constant 100.0 : f32 + %2 = fir.undefined tuple + %3 = constant 0 : index + %4 = fir.insert_value %2, %0, %3 : (tuple, i32, index) -> tuple + %5 = constant 1 : index + %6 = fir.insert_value %4, %1, %5 : (tuple, f32, index) -> tuple + fir.has_value %6 : tuple + } + }]; + + let arguments = (ins AnyType:$resval); + + let assemblyFormat = "$resval attr-dict `:` type($resval)"; +} + +// Operations on !fir.box type objects + +def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]> { + let summary = "boxes a given reference and (optional) dimension information"; + + let description = [{ + Create a boxed reference value. In Fortran, the implementation can require + extra information about an entity, such as its type, rank, etc. This + auxilliary information is packaged and abstracted as a value with box type + by the calling routine. (In Fortran, these are called descriptors.) + + %c1 = constant 1 : index + %c10 = constant 10 : index + %4 = fir.dims(%c1, %c10, %c1) : (index, index, index) -> !fir.dims<1> + %5 = ... : !fir.ref> + %6 = fir.embox %5, %4 : (!fir.ref>, !fir.dims<1>) -> !fir.box> + + The descriptor tuple may contain additional implementation-specific + information through the use of additional attributes. + }]; + + let arguments = (ins AnyReferenceLike:$memref, Variadic:$args); + + let results = (outs fir_BoxType); + + let parser = [{ + mlir::FunctionType type; + llvm::SmallVector operands; + mlir::OpAsmParser::OperandType memref; + if (parser.parseOperand(memref)) + return mlir::failure(); + operands.push_back(memref); + auto &builder = parser.getBuilder(); + if (!parser.parseOptionalLParen()) { + if (parser.parseOperandList(operands, + mlir::OpAsmParser::Delimiter::None) || + parser.parseRParen()) + return mlir::failure(); + auto lens = builder.getI32IntegerAttr(operands.size()); + result.addAttribute(lenpName(), lens); + } + if (!parser.parseOptionalComma()) { + mlir::OpAsmParser::OperandType dims; + if (parser.parseOperand(dims)) + return mlir::failure(); + operands.push_back(dims); + } else if (!parser.parseOptionalLSquare()) { + mlir::AffineMapAttr map; + if (parser.parseAttribute(map, layoutName(), result.attributes) || + parser.parseRSquare()) + return mlir::failure(); + } + if (parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(type) || + parser.resolveOperands(operands, type.getInputs(), + parser.getNameLoc(), result.operands) || + parser.addTypesToList(type.getResults(), result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(memref()); + if (hasLenParams()) { + p << '('; + p.printOperands(getLenParams()); + p << ')'; + } + if (getNumOperands() == 2) { + p << ", "; + p.printOperands(dims()); + } else if (auto map = getAttr(layoutName())) { + p << " [" << map << ']'; + } + p.printOptionalAttrDict(getAttrs(), {layoutName(), lenpName()}); + p << " : "; + p.printFunctionalType(getOperation()); + }]; + + let verifier = [{ + if (hasLenParams()) { + auto lenParams = numLenParams(); + auto eleTy = fir::dyn_cast_ptrEleTy(memref().getType()); + if (!eleTy) + return emitOpError("must embox a memory reference type"); + if (auto rt = eleTy.dyn_cast()) { + if (lenParams != rt.getNumLenParams()) + return emitOpError("number of LEN params does not correspond" + " to the !fir.type type"); + } else { + return emitOpError("LEN parameters require !fir.type type"); + } + for (auto lp : getLenParams()) + if (lp.getType().isa()) + return emitOpError("LEN parameters must be integral type"); + } + if (dims().size() == 0) { + // Ok. If there is no dims and no layout map, then emboxing a scalar. + // TODO: Should the type be enforced? It already must agree. + } else if (dims().size() == 1) { + auto d = *dims().begin(); + if (!d.getType().isa()) + return emitOpError("dimension argument must have !fir.dims type"); + } else { + return emitOpError("embox can only have one !fir.dim argument"); + } + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef layoutName() { return "layout_map"; } + static constexpr llvm::StringRef lenpName() { return "len_param_count"; } + bool hasLenParams() { return bool{getAttr(lenpName())}; } + unsigned numLenParams() { + if (auto x = getAttrOfType(lenpName())) + return x.getInt(); + return 0; + } + operand_range getLenParams() { + return {operand_begin(), operand_begin() + numLenParams()}; + } + operand_range dims() { + return {operand_begin() + numLenParams() + 1, operand_end()}; + } + }]; +} + +def fir_EmboxCharOp : fir_Op<"emboxchar", [NoSideEffect]> { + let arguments = (ins AnyReferenceLike:$memref, AnyIntegerLike:$len); + let results = (outs fir_BoxCharType); + + let summary = "boxes a given CHARACTER reference and its LEN parameter"; + + let description = [{ + Create a boxed CHARACTER value. The CHARACTER type has the LEN type + parameter, the value of which may only be known at runtime. Therefore, + a variable of type CHARACTER has both its data reference as well as a + LEN type parameter. + + CHARACTER(LEN=10) :: var + + %4 = ... : !fir.ref>> + %5 = constant 10 : i32 + %6 = fir.emboxchar %4, %5 : (!fir.ref>>, i32) -> !fir.boxchar<1> + + In the above `%4` is a memory reference to a buffer of 10 CHARACTER units. + This buffer and its LEN value (10) are wrapped into a pair in `%6`. + }]; + + let assemblyFormat = [{ + $memref `,` $len attr-dict `:` functional-type(operands, results) + }]; + + let verifier = [{ + auto eleTy = elementTypeOf(memref().getType()); + if (!eleTy.dyn_cast()) + return mlir::failure(); + return mlir::success(); + }]; +} + +def fir_EmboxProcOp : fir_Op<"emboxproc", [NoSideEffect]> { + + let summary = "boxes a given procedure and optional host context"; + + let description = [{ + Creates an abstract encapsulation of a PROCEDURE POINTER along with an + optional pointer to a host instance context. If the pointer is not to an + internal procedure or the internal procedure does not need a host context + then the form takes only the procedure's symbol. + + %0 = fir.emboxproc @f : ((i32) -> i32) -> !fir.boxproc<(i32) -> i32> + + An internal procedure requiring a host instance for correct execution uses + the second form. The closure of the host procedure's state is passed as a + reference to a tuple. It is the responsibility of the host to manage the + context's values accordingly, up to and including inhibiting register + promotion of local values. + + %4 = ... : !fir.ref> + %5 = fir.emboxproc @g, %4 : ((i32) -> i32, !fir.ref>) -> + !fir.boxproc<(i32) -> i32> + }]; + + let arguments = (ins SymbolRefAttr:$funcname, AnyReferenceLike:$host); + + let results = (outs fir_BoxProcType); + + let parser = [{ + mlir::SymbolRefAttr procRef; + if (parser.parseAttribute(procRef, "funcname", result.attributes)) + return mlir::failure(); + bool hasTuple = false; + mlir::OpAsmParser::OperandType tupleRef; + if (!parser.parseOptionalComma()) { + if (parser.parseOperand(tupleRef)) + return mlir::failure(); + hasTuple = true; + } + mlir::FunctionType type; + if (parser.parseColon() || + parser.parseLParen() || + parser.parseType(type)) + return mlir::failure(); + result.addAttribute("functype", mlir::TypeAttr::get(type)); + if (hasTuple) { + mlir::Type tupleType; + if (parser.parseComma() || + parser.parseType(tupleType) || + parser.resolveOperand(tupleRef, tupleType, result.operands)) + return mlir::failure(); + } + mlir::Type boxType; + if (parser.parseRParen() || + parser.parseArrow() || + parser.parseType(boxType) || + parser.addTypesToList(boxType, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr("funcname"); + auto h = host(); + if (h) { + p << ", "; + p.printOperand(h); + } + p << " : (" << getAttr("functype"); + if (h) + p << ", " << h.getType(); + p << ") -> " << getType(); + }]; + + let verifier = [{ + // host bindings (optional) must be a reference to a tuple + if (auto h = host()) { + if (auto r = h.getType().dyn_cast()) { + if (!r.getEleTy().dyn_cast()) + return mlir::failure(); + } else { + return mlir::failure(); + } + } + return mlir::success(); + }]; +} + +def fir_UnboxOp : fir_SimpleOp<"unbox", [NoSideEffect]> { + let summary = "unbox the boxed value into a tuple value"; + + let description = [{ + Unbox a boxed value into a result of multiple values from the box's + component data. The values are, minimally, a reference to the data of the + entity, the byte-size of one element, the rank, the type descriptor, a set + of flags (packed in an integer, and an array of dimension information (of + size rank). + + %40 = ... : !fir.box> + %41:6 = fir.unbox %40 : (!fir.box>) -> (!fir.ref>, i32, i32, !fir.tdesc>, i32, !fir.dims<4>) + }]; + + let arguments = (ins fir_BoxType:$box); + + let results = (outs + fir_ReferenceType, // pointer to data + AnyIntegerLike, // size of a data element + AnyIntegerLike, // rank of data + fir_TypeDescType, // abstract type descriptor + AnyIntegerLike, // attribute flags (bitfields) + fir_DimsType // dimension information (if any) + ); +} + +def fir_UnboxCharOp : fir_SimpleOp<"unboxchar", [NoSideEffect]> { + let summary = "unbox a boxchar value into a pair value"; + + let description = [{ + Unboxes a value of `boxchar` type into a pair consisting of a memory + reference to the CHARACTER data and the LEN type parameter. + + %45 = ... : !fir.boxchar<1> + %46:2 = fir.unboxchar %45 : (!fir.boxchar<1>) -> (!fir.ref>, i32) + }]; + + let arguments = (ins fir_BoxCharType:$boxchar); + + let results = (outs fir_ReferenceType, AnyIntegerLike); +} + +def fir_UnboxProcOp : fir_SimpleOp<"unboxproc", [NoSideEffect]> { + let summary = "unbox a boxproc value into a pair value"; + + let description = [{ + Unboxes a value of `boxproc` type into a pair consisting of a procedure + pointer and a pointer to a host context. + + %47 = ... : !fir.boxproc<() -> i32> + %48:2 = fir.unboxproc %47 : (!fir.ref<() -> i32>, !fir.ref>) + }]; + + let verifier = [{ + if (auto eleTy = fir::dyn_cast_ptrEleTy(refTuple().getType())) + if (eleTy.isa()) + return mlir::success(); + return emitOpError("second output argument has bad type"); + }]; + + let arguments = (ins fir_BoxProcType:$boxproc); + + let results = (outs FunctionType, fir_ReferenceType:$refTuple); +} + +def fir_BoxAddrOp : fir_SimpleOneResultOp<"box_addr", [NoSideEffect]> { + let summary = "return a memory reference to the boxed value"; + + let description = [{ + This operator is overloaded to work with values of type `box`, + `boxchar`, and `boxproc`. The result for each of these + cases, respectively, is the address of the data, the address of the + CHARACTER data, and the address of the procedure. + + %51 = fir.box_addr %box : (!fir.box) -> !fir.ref + %52 = fir.box_addr %boxchar : (!fir.boxchar<1>) -> !fir.ref> + %53 = fir.box_addr %boxproc : (!fir.boxproc) -> !fir.ref + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs AnyReferenceLike); +} + +def fir_BoxCharLenOp : fir_SimpleOp<"boxchar_len", [NoSideEffect]> { + let summary = "return the LEN type parameter from a boxchar value"; + + let description = [{ + Extracts the LEN type parameter from a `boxchar` value. + + %45 = ... : !boxchar<1> // CHARACTER(20) + %59 = fir.boxchar_len %45 : (!fir.boxchar<1>) -> i64 // len=20 + }]; + + let arguments = (ins fir_BoxCharType:$val); + + let results = (outs AnyIntegerLike); +} + +def fir_BoxDimsOp : fir_Op<"box_dims", [NoSideEffect]> { + let summary = "return the dynamic dimension information for the boxed value"; + + let description = [{ + Returns the triple of lower bound, extent, and stride for `dim` dimension + of `val`, which must have a `box` type. The dimensions are enumerated from + left to right from 0 to rank-1. This operation has undefined behavior if + `dim` is out of bounds. + + %c1 = constant 0 : i32 + %52:3 = fir.box_dims %40, %c1 : (!fir.box>, i32) -> (i32, i32, i32) + + The above is a request to return the left most row (at index 0) triple from + the box. The triple will be the lower bound, upper bound, and stride. + }]; + + let arguments = (ins fir_BoxType:$val, AnyIntegerLike:$dim); + + let results = (outs AnyIntegerLike, AnyIntegerLike, AnyIntegerLike); + + let assemblyFormat = [{ + $val `,` $dim attr-dict `:` functional-type(operands, results) + }]; + + let extraClassDeclaration = [{ + mlir::Type getTupleType(); + }]; +} + +def fir_BoxEleSizeOp : fir_SimpleOneResultOp<"box_elesize", [NoSideEffect]> { + let summary = "return the size of an element of the boxed value"; + + let description = [{ + Returns the size of an element in an entity of `box` type. This size may + not be known until runtime. + + %53 = fir.box_elesize %40 : (!fir.box, i32) -> i32 // size=4 + %54 = fir.box_elesize %40 : (!fir.box>, i32) -> i32 + + In the above example, `%53` may box an array of REAL values while `%54` + must box an array of REAL values (with dynamic rank and extent). + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs AnyIntegerLike); +} + +def fir_BoxIsAllocOp : fir_SimpleOp<"box_isalloc", [NoSideEffect]> { + let summary = "is the boxed value an ALLOCATABLE?"; + + let description = [{ + Determine if the boxed value was from an ALLOCATABLE entity. This will + return true if the originating box value was from a `fir.embox` op + with a mem-ref value that had the type !fir.heap. + + %r = ... : !fir.heap + %b = fir.embox %r : (!fir.heap) -> !fir.box + %a = fir.box_isalloc %b : (!fir.box) -> i1 // true + + The canonical descriptor implementation will carry a flag to record if the + variable is an ALLOCATABLE. + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs BoolLike); +} + +def fir_BoxIsArrayOp : fir_SimpleOp<"box_isarray", [NoSideEffect]> { + let summary = "is the boxed value an array?"; + + let description = [{ + Determine if the boxed value has a positive (> 0) rank. This will return + true if the originating box value was from a fir.embox with a memory + reference value that had the type !fir.array and/or a dims argument. + + %r = ... : !fir.ref + %d = fir.gendims(1, 100, 1) : (i32, i32, i32) -> !fir.dims<1> + %b = fir.embox %r, %d : (!fir.ref, !fir.dims<1>) -> !fir.box + %a = fir.box_isarray %b : (!fir.box) -> i1 // true + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs BoolLike); +} + +def fir_BoxIsPtrOp : fir_SimpleOp<"box_isptr", [NoSideEffect]> { + let summary = "is the boxed value a POINTER?"; + + let description = [{ + Determine if the boxed value was from a POINTER entity. + + %p = ... : !fir.ptr + %b = fir.embox %p : (!fir.ptr) -> !fir.box + %a = fir.box_isptr %b : (!fir.box) -> i1 // true + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs BoolLike); +} + +def fir_BoxProcHostOp : fir_SimpleOp<"boxproc_host", [NoSideEffect]> { + let summary = "returns the host instance pointer (or null)"; + + let description = [{ + Extract the host context pointer from a boxproc value. + + %8 = ... : !fir.boxproc<(!fir.ref>) -> i32> + %9 = fir.boxproc_host %8 : (!fir.boxproc<(!fir.ref>) -> i32>) -> !fir.ref> + + In the example, the reference to the closure over the host procedure's + variables is returned. This allows an internal procedure to access the + host's variables. It is up to lowering to determine the contract between + the host and the internal procedure. + }]; + + let arguments = (ins fir_BoxProcType:$val); + + let results = (outs fir_ReferenceType); +} + +def fir_BoxRankOp : fir_SimpleOneResultOp<"box_rank", [NoSideEffect]> { + let summary = "return the number of dimensions for the boxed value"; + + let description = [{ + Return the rank of a value of `box` type. If the value is scalar, the + rank is 0. + + %57 = fir.box_rank %40 : (!fir.box>) -> i32 + %58 = fir.box_rank %41 : (!fir.box) -> i32 + + The example `%57` shows how one would determine the rank of an array that + has deferred rank at runtime. This rank should be at least 1. In %58, the + descriptor may be either an array or a scalar, so the value is nonnegative. + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs AnyIntegerType); +} + +def fir_BoxTypeDescOp : fir_SimpleOneResultOp<"box_tdesc", [NoSideEffect]> { + let summary = "return the type descriptor for the boxed value"; + + let description = [{ + Return the opaque type descriptor of a value of `box` type. A type + descriptor is an implementation defined value that fully describes a type + to the Fortran runtime. + + %7 = fir.box_tdesc %41 : (!fir.box) -> !fir.tdesc + }]; + + let arguments = (ins fir_BoxType:$val); + + let results = (outs fir_TypeDescType); +} + +// Record and array type operations + +def fir_CoordinateOp : fir_Op<"coordinate_of", [NoSideEffect]> { + let summary = "Finds the coordinate (location) of a value in memory"; + + let description = [{ + Compute the internal coordinate address starting from a boxed value or + unboxed memory reference. Returns a memory reference. When computing the + coordinate of an array element, the rank of the array must be known and + the number of indexing expressions must equal the rank of the array. + + This operation will apply the access map from a boxed value implicitly. + + Unlike LLVM's GEP instruction, one cannot stride over the outermost + reference; therefore, the leading 0 index must be omitted. + + %i = ... : index + %h = ... : !fir.heap> + %p = fir.coordinate_of %h, %i : (!fir.heap>, index) -> !fir.ref + + In the example, `%p` will be a pointer to the `%i`-th f32 value in the + array `%h`. + }]; + + let arguments = (ins AnyRefOrBox:$ref, Variadic:$coor); + + let results = (outs fir_ReferenceType); + + let assemblyFormat = [{ + operands attr-dict `:` functional-type(operands, results) + }]; + + let verifier = [{ + // Recovering a LEN type parameter only makes sense from a boxed value + for (auto co : coor()) + if (auto *s = co.getDefiningOp()) + if (dyn_cast_or_null(s)) { + if (getNumOperands() != 2) + return emitOpError("len_param_index must be last argument"); + if (!ref().getType().dyn_cast()) + return emitOpError("len_param_index must be used on box type"); + } + return mlir::success(); + }]; +} + +def fir_ExtractValueOp : fir_OneResultOp<"extract_value", [NoSideEffect]> { + let summary = "Extract a value from an aggregate SSA-value"; + + let description = [{ + Extract a value from an entity with a type composed of tuples, arrays, + and/or derived types. Returns the value from entity with the type of the + specified component. Cannot be used on values of `!fir.box` type. + + Note that the entity ssa-value must be of compile-time known size in order + to use this operation. + + %f = fir.field_index field, !fir.type + %s = ... : !fir.type + %v = fir.extract_value %s, %f : (!fir.type, !fir.field) -> i32 + }]; + + let arguments = (ins + AnyCompositeLike:$adt, + Variadic:$coor + ); + + let assemblyFormat = [{ + $adt `,` $coor attr-dict `:` functional-type(operands, results) + }]; +} + +def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoSideEffect]> { + let summary = "create a field index value from a field identifier"; + + let description = [{ + Generate a field (offset) value from an identifier. Field values may be + lowered into exact offsets when the layout of a Fortran derived type is + known at compile-time. The type of a field value is `!fir.field` and + these values can be used with the `fir.coordinate_of`, `fir.extract_value`, + or `fir.insert_value` instructions to compute (abstract) addresses of + subobjects. + + %f = fir.field_index field, !fir.type + }]; + + let arguments = (ins + StrAttr:$field_id, + TypeAttr:$on_type, + Variadic:$lenparams + ); + + let parser = [{ + llvm::StringRef fieldName; + auto &builder = parser.getBuilder(); + mlir::Type recty; + if (parser.parseOptionalKeyword(&fieldName) || + parser.parseComma() || + parser.parseType(recty)) + return mlir::failure(); + result.addAttribute(fieldAttrName(), builder.getStringAttr(fieldName)); + if (!recty.dyn_cast()) + return mlir::failure(); + result.addAttribute(typeAttrName(), mlir::TypeAttr::get(recty)); + if (!parser.parseOptionalLParen()) { + llvm::SmallVector operands; + llvm::SmallVector types; + auto loc = parser.getNameLoc(); + if (parser.parseOperandList(operands, + mlir::OpAsmParser::Delimiter::None) || + parser.parseRParen() || + parser.parseColonTypeList(types) || + parser.resolveOperands(operands, types, loc, result.operands)) + return mlir::failure(); + } + mlir::Type fieldType = fir::FieldType::get(builder.getContext()); + if (parser.addTypeToList(fieldType, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' + << getAttrOfType(fieldAttrName()).getValue() << ", " + << getAttr(typeAttrName()); + if (getNumOperands()) { + p << '('; + p.printOperands(lenparams()); + auto sep = ") : "; + for (auto op : lenparams()) { + p << sep; + if (op) + p.printType(op.getType()); + else + p << "()"; + sep = ", "; + } + } + }]; + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, StringRef fieldName," + "Type recTy, ArrayRef operands = {}", + [{ + result.addAttribute(fieldAttrName(), builder->getStringAttr(fieldName)); + result.addAttribute(typeAttrName(), TypeAttr::get(recTy)); + result.addOperands(operands); + }] + >]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef fieldAttrName() { return "field_id"; } + static constexpr llvm::StringRef typeAttrName() { return "on_type"; } + }]; +} + +def fir_GenDimsOp : fir_OneResultOp<"gendims", [NoSideEffect]> { + + let summary = "generate a value of type `!fir.dims`"; + + let description = [{ + The arguments are an ordered list of integral type values that is a + multiple of 3 in length. Each such triple is defined as: the lower + index, the extent, and the stride for that dimension. The dimension + information is given in the same row-to-column order as Fortran. This + abstract dimension value must describe a reified object, so all dimension + information must be specified. The extent must be nonnegative and the + stride must not be zero. + + %d = fir.gendims %l, %u, %s : (index, index, index) -> !fir.dims<1> + }]; + + let arguments = (ins Variadic:$triples); + + let results = (outs fir_DimsType); + + let assemblyFormat = [{ + operands attr-dict `:` functional-type(operands, results) + }]; + + let verifier = [{ + auto size = triples().size(); + if (size < 1 && size <= 16 * 3) + return emitOpError("incorrect number of args"); + if (size % 3 != 0) + return emitOpError("requires a multiple of 3 args"); + return mlir::success(); + }]; +} + +def fir_InsertValueOp : fir_OneResultOp<"insert_value", [NoSideEffect]> { + let summary = "insert a new sub-value into a copy of an existing aggregate"; + + let description = [{ + Insert a value from an entity with a type composed of tuples, arrays, + and/or derived types. Returns a new ssa value with the same type as the + original entity. Cannot be used on values of `!fir.box` type. + + Note that the entity ssa-value must be of compile-time known size in order + to use this operation. + + %a = ... : !fir.array<10xtuple> + %f = ... : f32 + %o = ... : i32 + %c = constant 1 : i32 + %b = fir.insert_value %a, %f, %o, %c : (!fir.array<10x20xtuple>, f32, i32, i32) -> !fir.array<10x20xtuple> + }]; + + let arguments = (ins AnyCompositeLike:$adt, AnyType:$val, + Variadic:$coor); + let results = (outs AnyCompositeLike); + + let assemblyFormat = [{ + operands attr-dict `:` functional-type(operands, results) + }]; +} + +def fir_LenParamIndexOp : fir_OneResultOp<"len_param_index", [NoSideEffect]> { + let summary = + "create a field index value from a LEN type parameter identifier"; + + let description = [{ + Generate a LEN parameter (offset) value from an LEN parameter identifier. + The type of a LEN parameter value is `!fir.len` and these values can be + used with the `fir.coordinate_of` instructions to compute (abstract) + addresses of LEN parameters. + + %e = fir.len_param_index len1, !fir.type + %p = ... : !fir.box> + %q = fir.coordinate_of %p, %e : (!fir.box>, !fir.len) -> !fir.ref + }]; + + let arguments = (ins StrAttr:$field_id, TypeAttr:$on_type); + + let parser = [{ + llvm::StringRef fieldName; + auto &builder = parser.getBuilder(); + mlir::Type recty; + if (parser.parseOptionalKeyword(&fieldName) || + parser.parseComma() || + parser.parseType(recty)) + return mlir::failure(); + result.addAttribute(fieldAttrName(), builder.getStringAttr(fieldName)); + if (!recty.dyn_cast()) + return mlir::failure(); + result.addAttribute(typeAttrName(), mlir::TypeAttr::get(recty)); + mlir::Type lenType = fir::LenType::get(builder.getContext()); + if (parser.addTypeToList(lenType, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' + << getAttrOfType(fieldAttrName()).getValue() << ", " + << getAttr(typeAttrName()); + }]; + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, StringRef fieldName, Type recTy", + [{ + result.addAttribute(fieldAttrName(), builder->getStringAttr(fieldName)); + result.addAttribute(typeAttrName(), TypeAttr::get(recTy)); + }] + >]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef fieldAttrName() { return "field_id"; } + static constexpr llvm::StringRef typeAttrName() { return "on_type"; } + mlir::Type getOnType() { + return getAttrOfType(typeAttrName()).getValue(); + } + }]; +} + +// Fortran loops + +def ImplicitFirTerminator : SingleBlockImplicitTerminator<"FirEndOp">; + +def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> { + let summary = "generalized loop operation"; + let description = [{ + Generalized high-level looping construct. This operation is similar to + MLIR's `loop.for`. An ordered loop will return the final value of `%i`. + + %l = constant 0 : index + %u = constant 9 : index + fir.loop %i = %l to %u unordered { + %x = fir.convert %i : (index) -> i32 + %v = fir.call @compute(%x) : (i32) -> f32 + %p = fir.coordinate_of %A, %i : (!fir.ref, index) -> !fir.ref + fir.store %v to %p : !fir.ref + } + + The above example iterates over the interval `[%l, %u]`. The unordered + keyword indicates that the iterations can be executed in any order. + }]; + + let arguments = (ins + Index:$lowerBound, + Index:$upperBound, + Variadic:$optStep, + OptionalAttr:$constantStep, + OptionalAttr:$unordered + ); + + let results = (outs Variadic:$lastVal); + + let regions = (region SizedRegion<1>:$region); + + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<"mlir::Builder *builder, OperationState &result," + "int64_t lowerBound, int64_t upperBound, int64_t step = 1">, + OpBuilder<"mlir::Builder *builder, OperationState &result," + "mlir::Value lowerBound, mlir::Value upperBound," + "ArrayRef step = {}"> + ]; + + let parser = "return parseLoopOp(parser, result);"; + + let printer = [{ + p << getOperationName() << ' ' << getInductionVar() << " = " + << lowerBound() << " to " << upperBound(); + auto s = optStep(); + if (s.begin() != s.end()) { + p << " step "; + p.printOperand(*s.begin()); + } + if (unordered()) + p << " unordered"; + p.printRegion(region(), /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/false); + p.printOptionalAttrDict(getAttrs(), {unorderedKeyword(), stepKeyword()}); + }]; + + let verifier = [{ + auto step = optStep(); + if (step.begin() != step.end()) { + // FIXME: size of step must be 1 + auto *s = (*step.begin()).getDefiningOp(); + if (auto cst = dyn_cast_or_null(s)) + if (cst.getValue() == 0) + return emitOpError("constant step operand must be nonzero"); + } + + // Check that the body defines as single block argument for the induction + // variable. + auto *body = getBody(); + if (body->getNumArguments() != 1 || + !body->getArgument(0).getType().isIndex()) + return emitOpError("expected body to have a single index argument for " + "the induction variable"); + if (lastVal().size() > 1) + return emitOpError("can only return one final value of iterator"); + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + static constexpr const char *unorderedKeyword() { return "unordered"; } + static constexpr const char *stepKeyword() { return "step"; } + + /// Is this an unordered loop? + bool isUnordered() { return getAttr(unorderedKeyword()).isa(); } + + /// Does loop set (and return) the final value of the control variable? + bool hasLastValue() { return lastVal().size(); } + + /// Get the body of the loop + mlir::Block *getBody() { return ®ion().front(); } + + /// Get the block argument corresponding to the loop control value (PHI) + mlir::Value getInductionVar() { return getBody()->getArgument(0); } + + /// Get a builder to insert operations into the LoopOp + mlir::OpBuilder getBodyBuilder() { + return mlir::OpBuilder(getBody(), std::prev(getBody()->end())); + } + + void setLowerBound(mlir::Value bound) { + getOperation()->setOperand(0, bound); + } + + void setUpperBound(mlir::Value bound) { + getOperation()->setOperand(1, bound); + } + + void setStep(mlir::Value step) { + getOperation()->setOperand(2, step); + } + }]; +} + +def fir_WhereOp : fir_Op<"where", [ImplicitFirTerminator]> { + let summary = "generalized conditional operation"; + let description = [{ + To conditionally execute operations (typically) within the body of a + `fir.loop` operation. This operation is similar to `loop.if`. + + %56 = ... : i1 + %78 = ... : !fir.ref + fir.where %56 { + fir.store %76 to %78 : !fir.ref + } otherwise { + fir.store %77 to %78 : !fir.ref + } + }]; + + let arguments = (ins I1:$condition); + + let regions = (region SizedRegion<1>:$whereRegion, AnyRegion:$otherRegion); + + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<"Builder *builder, OperationState &result, " + "Value cond, bool withOtherRegion"> + ]; + + let parser = [{ return parseWhereOp(parser, result); }]; + + let printer = [{ + p << getOperationName() << ' ' << condition(); + p.printRegion(whereRegion(), /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/false); + + // Print the 'else' regions if it exists and has a block. + auto &otherReg = otherRegion(); + if (!otherReg.empty()) { + p << " otherwise"; + p.printRegion(otherReg, /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/false); + } + p.printOptionalAttrDict(getAttrs()); + }]; + + let verifier = [{ + for (auto ®ion : getOperation()->getRegions()) { + if (region.empty()) + continue; + for (auto &b : region) + if (b.getNumArguments() != 0) + return emitOpError("requires that child entry blocks have no args"); + } + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + mlir::OpBuilder getWhereBodyBuilder() { + assert(!whereRegion().empty() && "Unexpected empty 'where' region."); + mlir::Block &body = whereRegion().front(); + return mlir::OpBuilder(&body, std::prev(body.end())); + } + mlir::OpBuilder getOtherBodyBuilder() { + assert(!otherRegion().empty() && "Unexpected empty 'other' region."); + mlir::Block &body = otherRegion().front(); + return mlir::OpBuilder(&body, std::prev(body.end())); + } + }]; +} + +// Procedure call operations + +def fir_CallOp : fir_Op<"call", []> { + let summary = "call a procedure"; + + let description = [{ + Call the specified function or function reference. + + Provides a custom parser and pretty printer to allow a more readable syntax + in the FIR dialect, e.g. `fir.call @sub(%12)` or `fir.call %20(%22,%23)`. + + %a = fir.call %funcref(%arg0) : (!fir.ref) -> f32 + %b = fir.call @function(%arg1, %arg2) : (!fir.ref, !fir.ref) -> f32 + }]; + + let arguments = (ins + OptionalAttr:$callee, + Variadic:$args + ); + + let results = (outs Variadic); + + let parser = "return parseCallOp(parser, result);"; + let printer = "printCallOp(p, *this);"; +} + +def fir_DispatchOp : fir_Op<"dispatch", []> { + let summary = "call a type-bound procedure"; + + let description = [{ + Perform a dynamic dispatch on the method name via the dispatch table + associated with the first argument. The attribute 'pass_arg_pos' can be + used to select a dispatch argument other than the first one. + + %r = fir.dispatch methodA(%o) : (!fir.box) -> i32 + }]; + + let arguments = (ins + StrAttr:$method, + fir_BoxType:$object, + Variadic:$args + ); + + let results = (outs Variadic); + + let parser = [{ + mlir::FunctionType calleeType; + llvm::SmallVector operands; + auto calleeLoc = parser.getNameLoc(); + llvm::StringRef calleeName; + if (parser.parseOptionalKeyword(&calleeName)) { + mlir::StringAttr calleeAttr; + if (parser.parseAttribute(calleeAttr, "method", result.attributes)) + return mlir::failure(); + } else { + result.addAttribute("method", + parser.getBuilder().getStringAttr(calleeName)); + } + if (parser.parseOperandList(operands, + mlir::OpAsmParser::Delimiter::Paren) || + parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(calleeType) || + parser.addTypesToList(calleeType.getResults(), result.types) || + parser.resolveOperands( + operands, calleeType.getInputs(), calleeLoc, result.operands)) + return mlir::failure(); + result.addAttribute("fn_type", mlir::TypeAttr::get(calleeType)); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr("method") << '('; + p.printOperand(object()); + if (arg_operand_begin() != arg_operand_end()) { + p << ", "; + p.printOperands(args()); + } + p << ')'; + p.printOptionalAttrDict(getAttrs(), {"fn_type", "method"}); + auto resTy{getResultTypes()}; + llvm::SmallVector argTy(getOperandTypes()); + p << " : " << mlir::FunctionType::get(argTy, resTy, getContext()); + }]; + + let extraClassDeclaration = [{ + mlir::FunctionType getFunctionType(); + operand_range getArgOperands() { + return {arg_operand_begin(), arg_operand_end()}; + } + operand_iterator arg_operand_begin() { return operand_begin() + 1; } + operand_iterator arg_operand_end() { return operand_end(); } + llvm::StringRef passArgAttrName() { return "pass_arg_pos"; } + unsigned passArgPos(); + }]; +} + +// Constant operations that support Fortran + +def fir_StringLitOp : fir_Op<"string_lit", [NoSideEffect]>, + Results<(outs fir_SequenceType)> { + let summary = "create a string literal constant"; + + let description = [{ + An FIR constant that represents a sequence of characters that correspond + to Fortran's CHARACTER type, including a LEN. We support CHARACTER values + of different KINDs (different constant sizes). + + Example: + + %1 = fir.string_lit "Hello, World!"(13) : !fir.char<1> // ASCII + %2 = fir.string_lit [158, 2345](2) : !fir.char<2> // Wide chars + }]; + + let parser = [{ + auto &builder = parser.getBuilder(); + mlir::Attribute val; + llvm::SmallVector attrs; + if (parser.parseAttribute(val, "fake", attrs)) + return mlir::failure(); + if (auto v = val.dyn_cast()) + result.attributes.push_back(builder.getNamedAttr(value(), v)); + else if (auto v = val.dyn_cast()) + result.attributes.push_back(builder.getNamedAttr(xlist(), v)); + else + return mlir::failure(); + mlir::IntegerAttr sz; + mlir::Type type; + if (parser.parseLParen() || + parser.parseAttribute(sz, size(), result.attributes) || + parser.parseRParen() || + parser.parseColonType(type)) + return mlir::failure(); + type = fir::SequenceType::get({sz.getInt()}, type); + if (!type || + parser.addTypesToList(type, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getValue() << '('; + p << getSize().cast().getValue() << ") : "; + p.printType(getType().cast().getEleTy()); + }]; + + let verifier = [{ + if (getSize().cast().getValue().isNegative()) + return emitOpError("size must be non-negative"); + auto eleTy = getType().cast().getEleTy(); + if (!eleTy.isa()) + return emitOpError("must have !fir.char type"); + if (auto xl = getAttr(xlist())) { + auto xList = xl.cast(); + for (auto a : xList) + if (!a.isa()) + return emitOpError("values in list must be integers"); + } + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + static constexpr const char *size() { return "size"; } + static constexpr const char *value() { return "value"; } + static constexpr const char *xlist() { return "xlist"; } + + // Get the LEN attribute of this character constant + mlir::Attribute getSize() { return getAttr(size()); } + // Get the string value of this character constant + mlir::Attribute getValue() { + if (auto attr = getAttr(value())) + return attr; + return getAttr(xlist()); + } + + /// Is this a wide character literal (1 character > 8 bits) + bool isWideValue(); + }]; +} + +// Complex operations + +class fir_ArithmeticOp traits = []> : + fir_Op, + Results<(outs AnyType)> { + let parser = [{ + return impl::parseOneResultSameOperandTypeOp(parser, result); + }]; + + let printer = [{ return fir::printBinaryOp(this->getOperation(), p); }]; +} + +class fir_UnaryArithmeticOp traits = []> : + fir_Op, + Results<(outs AnyType)> { + let parser = [{ + return impl::parseOneResultSameOperandTypeOp(parser, result); + }]; + + let printer = [{ return fir::printUnaryOp(this->getOperation(), p); }]; +} + +def fir_ConstfOp : fir_Op<"constf", [NoSideEffect]>, + Results<(outs fir_RealType)> { + let summary = "create a floating point constant"; + + let description = [{ + A floating-point constant. This operation is to augment MLIR to be able + to represent APFloat values that are not supported in the standard dialect. + }]; + + let parser = [{ + fir::RealAttr flt; + mlir::Type type; + if (parser.parseLParen() || + parser.parseAttribute(flt, constAttrName(), result.attributes) || + parser.parseRParen() || parser.parseColonType(type) || + parser.addTypesToList(type, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr(constAttrName()) << " : "; + p.printType(getType()); + }]; + + let verifier = [{ + if (!getType().isa()) + return emitOpError("must be a !fir.real type"); + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef constAttrName() { return "constant"; } + + mlir::Attribute getValue() { return getAttr(constAttrName()); } + }]; +} + +class RealUnaryArithmeticOp traits = []> : + fir_UnaryArithmeticOp, + Arguments<(ins AnyRealLike:$operand)>; + +def fir_NegfOp : RealUnaryArithmeticOp<"negf">; + +class RealArithmeticOp traits = []> : + fir_ArithmeticOp, + Arguments<(ins AnyRealLike:$lhs, AnyRealLike:$rhs)>; + +def fir_AddfOp : RealArithmeticOp<"addf", [Commutative]>; +def fir_SubfOp : RealArithmeticOp<"subf">; +def fir_MulfOp : RealArithmeticOp<"mulf", [Commutative]>; +def fir_DivfOp : RealArithmeticOp<"divf">; +def fir_ModfOp : RealArithmeticOp<"modf">; +// Pow is a builtin call and not a primitive + +def fir_CmpfOp : fir_Op<"cmpf", + [NoSideEffect, SameTypeOperands, SameOperandsAndResultShape]> { + let summary = "floating-point comparison operator"; + + let description = [{ + Extends the standard floating-point comparison to handle the extended + floating-point types found in FIR. + }]; + + let arguments = (ins AnyRealLike:$lhs, AnyRealLike:$rhs); + + let results = (outs AnyLogicalLike); + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, CmpFPredicate predicate," + "Value lhs, Value rhs", [{ + fir::buildCmpFOp(builder, result, predicate, lhs, rhs); + }]>]; + + let parser = [{ return fir::parseCmpfOp(parser, result); }]; + + let printer = [{ fir::printCmpfOp(p, *this); }]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef getPredicateAttrName() { + return "predicate"; + } + static CmpFPredicate getPredicateByName(llvm::StringRef name); + + CmpFPredicate getPredicate() { + return (CmpFPredicate)getAttrOfType( + getPredicateAttrName()).getInt(); + } + }]; +} + +def fir_ConstcOp : fir_Op<"constc", [NoSideEffect]>, + Results<(outs fir_ComplexType)> { + let summary = "create a complex constant"; + + let description = [{ + A complex constant. Similar to the standard dialect complex type, but this + extension allows constants with APFloat values that are not supported in + the standard dialect. + }]; + + let parser = [{ + fir::RealAttr realp; + fir::RealAttr imagp; + mlir::Type type; + if (parser.parseLParen() || + parser.parseAttribute(realp, realAttrName(), result.attributes) || + parser.parseComma() || + parser.parseAttribute(imagp, imagAttrName(), result.attributes) || + parser.parseRParen() || + parser.parseColonType(type) || + parser.addTypesToList(type, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << " (0x"; + auto f1 = getAttr(realAttrName()).cast(); + auto i1 = f1.getValue().bitcastToAPInt(); + p.getStream().write_hex(i1.getZExtValue()); + p << ", 0x"; + auto f2 = getAttr(imagAttrName()).cast(); + auto i2 = f2.getValue().bitcastToAPInt(); + p.getStream().write_hex(i2.getZExtValue()); + p << ") : "; + p.printType(getType()); + }]; + + let verifier = [{ + if (!getType().isa()) + return emitOpError("must be a !fir.complex type"); + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef realAttrName() { return "real"; } + static constexpr llvm::StringRef imagAttrName() { return "imaginary"; } + + mlir::Attribute getReal() { return getAttr(realAttrName()); } + mlir::Attribute getImaginary() { return getAttr(imagAttrName()); } + }]; +} + +class ComplexUnaryArithmeticOp traits = []> : + fir_UnaryArithmeticOp, + Arguments<(ins fir_ComplexType:$operand)>; + +def fir_NegcOp : ComplexUnaryArithmeticOp<"negc">; + +class ComplexArithmeticOp traits = []> : + fir_ArithmeticOp, + Arguments<(ins fir_ComplexType:$lhs, fir_ComplexType:$rhs)>; + +def fir_AddcOp : ComplexArithmeticOp<"addc", [Commutative]>; +def fir_SubcOp : ComplexArithmeticOp<"subc">; +def fir_MulcOp : ComplexArithmeticOp<"mulc", [Commutative]>; +def fir_DivcOp : ComplexArithmeticOp<"divc">; +// Pow is a builtin call and not a primitive + +def fir_CmpcOp : fir_Op<"cmpc", + [NoSideEffect, SameTypeOperands, SameOperandsAndResultShape]> { + let summary = "complex floating-point comparison operator"; + + let description = [{ + A complex comparison to handle complex types found in FIR. + }]; + + let arguments = (ins fir_ComplexType:$lhs, fir_ComplexType:$rhs); + + let results = (outs AnyLogicalLike); + + let parser = "return fir::parseCmpcOp(parser, result);"; + + let printer = "fir::printCmpcOp(p, *this);"; + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, CmpFPredicate predicate," + "Value lhs, Value rhs", [{ + fir::buildCmpCOp(builder, result, predicate, lhs, rhs); + }]>]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef getPredicateAttrName() { + return "predicate"; + } + + CmpFPredicate getPredicate() { + return (CmpFPredicate)getAttrOfType( + getPredicateAttrName()).getInt(); + } + }]; +} + +// Other misc. operations + +def fir_AddrOfOp : fir_OneResultOp<"address_of", [NoSideEffect]> { + let summary = "convert a symbol to an SSA value"; + let description = [{ + Convert a symbol (a function or global reference) to an SSA-value to be + used in other Operations. + + %p = fir.address_of(@symbol) : !fir.ref + }]; + + let arguments = (ins SymbolRefAttr:$symbol); + + let parser = [{ + mlir::SymbolRefAttr attr; + mlir::Type type; + if (parser.parseLParen() || + parser.parseAttribute(attr, "symbol", result.attributes) || + parser.parseRParen() || + parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(type) || + parser.addTypeToList(type, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << " (" << getAttr("symbol") << ')'; + p.printOptionalAttrDict(getAttrs(), {"symbol"}); + p << " : " << getType(); + }]; +} + +def fir_ConvertOp : fir_OneResultOp<"convert", [NoSideEffect]> { + let summary = "encapsulates all Fortran scalar type conversions"; + let description = [{ + Generalized type conversion. Convert the ssa value from type T to type U. + Not all pairs of types have conversions. When types T and U are the same + type, this instruction is a NOP and may be folded away. + + %v = ... : i64 + %w = fir.convert %v : (i64) -> i32 + + The example truncates the value `%v` from an i64 to an i32. + }]; + + let arguments = (ins AnyType:$value); + + let assemblyFormat = [{ + $value attr-dict `:` functional-type($value, results) + }]; +} + +def FortranTypeAttr : Attr()">, + Or<[CPred<"$_self.cast().getValue().isa()">, + CPred<"$_self.cast().getValue().isa()">, + CPred<"$_self.cast().getValue().isa()">, + CPred<"$_self.cast().getValue().isa()">, + CPred<"$_self.cast().getValue().isa()">, + CPred<"$_self.cast().getValue().isa()">]>]>, + "Fortran surface type"> { + let storageType = [{ TypeAttr }]; + let returnType = "Type"; + let convertFromStorage = "$_self.getValue().cast()"; +} + +def fir_GenTypeDescOp : fir_OneResultOp<"gentypedesc", [NoSideEffect]> { + let summary = "generate a type descriptor for a given type"; + let description = [{ + Generates a constant object that is an abstract type descriptor of the + specified type. The meta-type of a type descriptor for the type `T` + is `!fir.tdesc`. + + !T = type !fir.type + %t = fir.gentypedesc !T // returns value of !fir.tdesc + }]; + + let arguments = (ins FortranTypeAttr:$in_type); + + let parser = [{ + mlir::Type intype; + if (parser.parseType(intype)) + return mlir::failure(); + result.addAttribute("in_type", mlir::TypeAttr::get(intype)); + mlir::Type restype = TypeDescType::get(intype); + if (parser.addTypeToList(restype, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr("in_type"); + p.printOptionalAttrDict(getAttrs(), {"in_type"}); + }]; + + let builders = [ + OpBuilder<"Builder *, OperationState &result, mlir::TypeAttr inty"> + ]; + + let verifier = [{ + mlir::Type resultTy = getType(); + if (auto tdesc = resultTy.dyn_cast()) { + if (tdesc.getOfTy() != getInType()) + return emitOpError("wrapped type mismatched"); + } else { + return emitOpError("must be !fir.tdesc type"); + } + return mlir::success(); + }]; + + let extraClassDeclaration = [{ + mlir::Type getInType() { + // get the type that the type descriptor describes + return getAttrOfType("in_type").getValue(); + } + }]; +} + +def fir_NoReassocOp : fir_OneResultOp<"no_reassoc", + [SameOperandsAndResultType]> { + let summary = "synthetic op to prevent reassociation"; + let description = [{ + Primitive operation meant to intrusively prevent operator reassociation. + The operation is otherwise a nop and the value returned is the same as the + argument. + + The presence of this operation prevents any local optimizations. In the + example below, this would prevent possibly replacing the multiply and add + operations with a single FMA operation. + + %98 = mulf %96, %97 : f32 + %99 = fir.no_reassoc %98 : f32 + %a0 = addf %99, %95 : f32 + }]; + + let arguments = (ins AnyType:$val); + + let assemblyFormat = "$val attr-dict `:` type($val)"; +} + +class AtMostRegion : Region< + CPred<"$_self.getBlocks().size() <= " # numBlocks>, + "region with " # numBlocks # " blocks">; + +def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, Symbol]> { + let summary = "Global data"; + let description = [{ + A global variable or constant with initial values. + + The example creates a global variable (writable) named + `@_QV_Mquark_Vvarble` with some initial values. The initializer should + conform to the variable's type. + + fir.global @_QV_Mquark_Vvarble : tuple { + %1 = constant 1 : i32 + %2 = constant 2.0 : f32 + %3 = fir.undefined tuple + %z = constant 0 : index + %o = constant 1 : index + %4 = fir.insert_value %3, %1, %z : (tuple, i32, index) -> tuple + %5 = fir.insert_value %4, %1, %o : (tuple, f32, index) -> tuple + return %5 + } + }]; + + let arguments = (ins + StrAttr:$sym_name, + OptionalAttr:$initval, + UnitAttr:$constant, + TypeAttr:$type + ); + + let results = (outs fir_ReferenceType:$resultType); + + let regions = (region AtMostRegion<1>:$region); + + let parser = [{ + // Parse the name as a symbol reference attribute. + SymbolRefAttr nameAttr; + if (parser.parseAttribute(nameAttr, mlir::SymbolTable::getSymbolAttrName(), + result.attributes)) + return failure(); + + auto &builder = parser.getBuilder(); + auto name = nameAttr.getRootReference(); + result.attributes.back().second = builder.getStringAttr(name); + + bool simpleInitializer = false; + if (!parser.parseOptionalLParen()) { + Attribute attr; + if (parser.parseAttribute(attr, initValAttrName(), result.attributes) || + parser.parseRParen()) + return failure(); + simpleInitializer = true; + } + + if (!parser.parseOptionalKeyword(constantAttrName())) { + // if "constant" keyword then mark this as a constant, not a variable + result.addAttribute(constantAttrName(), builder.getUnitAttr()); + } + + mlir::Type globalType; + if (parser.parseColonType(globalType)) + return failure(); + + result.addAttribute(typeAttrName(), mlir::TypeAttr::get(globalType)); + + if (!simpleInitializer) { + // Parse the optional initializer body. + if (parser.parseRegion(*result.addRegion(), llvm::None, llvm::None)) + return failure(); + } + + auto refTy = AllocaOp::wrapResultType(globalType); + if (parser.addTypeToList(refTy, result.types)) + return failure(); + return success(); + }]; + + let printer = [{ + auto varName = getAttrOfType( + mlir::SymbolTable::getSymbolAttrName()).getValue(); + p << getOperationName() << " @" << varName; + if (auto iv = initval().getValueOr(Attribute())) { + p << '('; + p.printAttribute(iv); + p << ')'; + } + if (getAttr(constantAttrName())) + p << ' ' << constantAttrName(); + p << " : "; + p.printType(getType()); + Region &body = getOperation()->getRegion(0); + if (!body.empty()) + p.printRegion(body, /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/true); + }]; + + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<"mlir::Builder *builder, OperationState &result," + "StringRef name, Type type, ArrayRef attrs = {}", + [{ + result.addAttribute(typeAttrName(), mlir::TypeAttr::get(type)); + result.addAttribute(mlir::SymbolTable::getSymbolAttrName(), + builder->getStringAttr(name)); + for (const auto &pair : attrs) + result.addAttribute(pair.first, pair.second); + }]> + ]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef constantAttrName() { return "constant"; } + static constexpr llvm::StringRef initValAttrName() { return "initval"; } + static constexpr llvm::StringRef typeAttrName() { return "type"; } + + mlir::Type getType() { + return getAttrOfType(typeAttrName()).getValue(); + } + + /// Append the next initializer value to the `GlobalOp` to construct + /// the variable's initial value. + void appendInitialValue(mlir::Operation *op); + + /// A GlobalOp has one region. + mlir::Region &getRegion() { return getOperation()->getRegion(0); } + + /// A GlobalOp has one block. + mlir::Block &getBlock() { return getRegion().front(); } + }]; +} + +def fir_GlobalLenOp : fir_Op<"global_len", []> { + let summary = "map a LEN parameter to a global"; + let description = [{ + A global entity (that is not an automatic data object) can have extra LEN + parameter (compile-time) constants associated with the instance's type. + These values can be bound to the global instance used `fir.global_len`. + + global @g : !fir.type { + fir.global_len len1, 10 : i32 + %1 = fir.undefined : !fir.type + return %1 : !fir.type + } + }]; + + let arguments = (ins StrAttr:$lenparam, APIntAttr:$intval); + + let parser = [{ + llvm::StringRef fieldName; + if (parser.parseOptionalKeyword(&fieldName)) { + mlir::StringAttr fieldAttr; + if (parser.parseAttribute(fieldAttr, lenParamAttrName(), + result.attributes)) + return mlir::failure(); + } else { + result.addAttribute(lenParamAttrName(), + parser.getBuilder().getStringAttr(fieldName)); + } + mlir::IntegerAttr constant; + if (parser.parseComma() || + parser.parseAttribute(constant, intAttrName(), result.attributes)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr(lenParamAttrName()) << ", " + << getAttr(intAttrName()); + }]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef lenParamAttrName() { return "lenparam"; } + static constexpr llvm::StringRef intAttrName() { return "intval"; } + }]; +} + +def fir_DispatchTableOp : fir_Op<"dispatch_table", [IsolatedFromAbove, Symbol, + ImplicitFirTerminator]> { + let summary = "Dispatch table definition"; + + let description = [{ + Define a dispatch table for a derived type with type-bound procedures. + + A dispatch table is an untyped symbol that contains a list of associations + between method identifiers and corresponding `FuncOp` symbols. + + The ordering of associations in the map is determined by the front-end. + + fir.dispatch_table @_QDTMquuzTfoo { + fir.dt_entry method1, @_QFNMquuzTfooPmethod1AfooR + fir.dt_entry method2, @_QFNMquuzTfooPmethod2AfooII + } + }]; + + let parser = [{ + // Parse the name as a symbol reference attribute. + SymbolRefAttr nameAttr; + if (parser.parseAttribute(nameAttr, mlir::SymbolTable::getSymbolAttrName(), + result.attributes)) + return failure(); + + // Convert the parsed name attr into a string attr. + result.attributes.back().second = + parser.getBuilder().getStringAttr(nameAttr.getRootReference()); + + // Parse the optional table body. + mlir::Region *body = result.addRegion(); + if (parser.parseOptionalRegion(*body, llvm::None, llvm::None)) + return mlir::failure(); + + ensureTerminator(*body, parser.getBuilder(), result.location); + return mlir::success(); + }]; + + let printer = [{ + auto tableName = getAttrOfType( + mlir::SymbolTable::getSymbolAttrName()).getValue(); + p << getOperationName() << " @" << tableName; + + Region &body = getOperation()->getRegion(0); + if (!body.empty()) + p.printRegion(body, /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/false); + }]; + + let verifier = [{ + for (auto &op : getBlock()) + if (!(isa(op) || isa(op))) + return emitOpError("dispatch table must contain dt_entry"); + return mlir::success(); + }]; + + let regions = (region SizedRegion<1>:$region); + + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<"mlir::Builder *builder, OperationState *result," + "StringRef name, Type type, ArrayRef attrs = {}", + [{ + result->addAttribute(mlir::SymbolTable::getSymbolAttrName(), + builder->getStringAttr(name)); + for (const auto &pair : attrs) + result->addAttribute(pair.first, pair.second); + }]> + ]; + + let extraClassDeclaration = [{ + /// Append a dispatch table entry to the table. + void appendTableEntry(mlir::Operation *op); + + mlir::Region &getRegion() { + return this->getOperation()->getRegion(0); + } + + mlir::Block &getBlock() { + return getRegion().front(); + } + }]; +} + +def fir_DTEntryOp : fir_Op<"dt_entry", []> { + let summary = "map entry in a dispatch table"; + + let description = [{ + An entry in a dispatch table. Allows a function symbol to be bound + to a specifier method identifier. A dispatch operation uses the dynamic + type of a distinguished argument to determine an exact dispatch table + and uses the method identifier to select the type-bound procedure to + be called. + + fir.dt_entry method_name, @uniquedProcedure + }]; + + let arguments = (ins StrAttr:$method, SymbolRefAttr:$proc); + + let parser = [{ + llvm::StringRef methodName; + // allow `methodName` or `"methodName"` + if (parser.parseOptionalKeyword(&methodName)) { + mlir::StringAttr methodAttr; + if (parser.parseAttribute(methodAttr, methodAttrName(), + result.attributes)) + return mlir::failure(); + } else { + result.addAttribute(methodAttrName(), + parser.getBuilder().getStringAttr(methodName)); + } + mlir::SymbolRefAttr calleeAttr; + if (parser.parseComma() || + parser.parseAttribute(calleeAttr, procAttrName(), result.attributes)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr(methodAttrName()) << ", " + << getAttr(procAttrName()); + }]; + + let extraClassDeclaration = [{ + static constexpr llvm::StringRef methodAttrName() { return "method"; } + static constexpr llvm::StringRef procAttrName() { return "proc"; } + }]; +} + +#endif diff --git a/include/flang/Optimizer/Dialect/FIROpsSupport.h b/include/flang/Optimizer/Dialect/FIROpsSupport.h new file mode 100644 index 000000000000..81807f5bf837 --- /dev/null +++ b/include/flang/Optimizer/Dialect/FIROpsSupport.h @@ -0,0 +1,73 @@ +//===-- optimizer/Dialect/FIROpsSupport.h -- FIR op support -----*- 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 OPTIMIZER_DIALECT_FIROPSSUPPORT_H +#define OPTIMIZER_DIALECT_FIROPSSUPPORT_H + +#include "flang/Optimizer/Dialect/FIROps.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" + +namespace fir { + +/// return true iff the Operation is a non-volatile LoadOp +inline bool nonVolatileLoad(mlir::Operation *op) { + if (auto load = dyn_cast(op)) + return !load.getAttr("volatile"); + return false; +} + +/// return true iff the Operation is a fir::CallOp, fir::DispatchOp, +/// mlir::CallOp, or mlir::CallIndirectOp and not pure +/// NB: this is not the same as `!pureCall(op)` +inline bool impureCall(mlir::Operation *op) { + // Should we also auto-detect that the called function is pure if its + // arguments are not references? For now, rely on a "pure" attribute. + if (auto call = dyn_cast(op)) + return !call.getAttr("pure"); + if (auto dispatch = dyn_cast(op)) + return !dispatch.getAttr("pure"); + if (auto call = dyn_cast(op)) + return !call.getAttr("pure"); + if (auto icall = dyn_cast(op)) + return !icall.getAttr("pure"); + return false; +} + +/// return true iff the Operation is a fir::CallOp, fir::DispatchOp, +/// mlir::CallOp, or mlir::CallIndirectOp and is also pure. +/// NB: this is not the same as `!impureCall(op)` +inline bool pureCall(mlir::Operation *op) { + // Should we also auto-detect that the called function is pure if its + // arguments are not references? For now, rely on a "pure" attribute. + if (auto call = dyn_cast(op)) + return bool(call.getAttr("pure")); + if (auto dispatch = dyn_cast(op)) + return bool(dispatch.getAttr("pure")); + if (auto call = dyn_cast(op)) + return bool(call.getAttr("pure")); + if (auto icall = dyn_cast(op)) + return bool(icall.getAttr("pure")); + return false; +} + +/// Get or create a FuncOp in a module. +/// +/// If `module` already contains FuncOp `name`, it is returned. Otherwise, a new +/// FuncOp is created, and that new FuncOp is returned. +mlir::FuncOp createFuncOp(mlir::Location loc, mlir::ModuleOp module, + llvm::StringRef name, mlir::FunctionType type, + llvm::ArrayRef attrs = {}); + +/// Get or create a GlobalOp in a module. +fir::GlobalOp createGlobalOp(mlir::Location loc, mlir::ModuleOp module, + llvm::StringRef name, mlir::Type type, + llvm::ArrayRef attrs = {}); + +} // namespace fir + +#endif // OPTIMIZER_DIALECT_FIROPSSUPPORT_H diff --git a/include/flang/Optimizer/Dialect/FIRType.h b/include/flang/Optimizer/Dialect/FIRType.h new file mode 100644 index 000000000000..49764a7c3e66 --- /dev/null +++ b/include/flang/Optimizer/Dialect/FIRType.h @@ -0,0 +1,357 @@ +//===-- optimizer/Dialect/FIRType.h -- FIR types ----------------*- 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 OPTIMIZER_DIALECT_FIRTYPE_H +#define OPTIMIZER_DIALECT_FIRTYPE_H + +#include "mlir/IR/Attributes.h" +#include "mlir/IR/Types.h" +#include "llvm/ADT/SmallVector.h" + +namespace llvm { +class raw_ostream; +class StringRef; +template +class ArrayRef; +class hash_code; +} // namespace llvm + +namespace mlir { +class DialectAsmParser; +class DialectAsmPrinter; +} // namespace mlir + +namespace fir { + +class FIROpsDialect; + +using KindTy = int; + +namespace detail { +struct BoxTypeStorage; +struct BoxCharTypeStorage; +struct BoxProcTypeStorage; +struct CharacterTypeStorage; +struct CplxTypeStorage; +struct DimsTypeStorage; +struct FieldTypeStorage; +struct HeapTypeStorage; +struct IntTypeStorage; +struct LenTypeStorage; +struct LogicalTypeStorage; +struct PointerTypeStorage; +struct RealTypeStorage; +struct RecordTypeStorage; +struct ReferenceTypeStorage; +struct SequenceTypeStorage; +struct TypeDescTypeStorage; +} // namespace detail + +/// Integral identifier for all the types comprising the FIR type system +enum TypeKind { + // The enum starts at the range reserved for this dialect. + FIR_TYPE = mlir::Type::FIRST_FIR_TYPE, + FIR_BOX, // (static) descriptor + FIR_BOXCHAR, // CHARACTER pointer and length + FIR_BOXPROC, // procedure with host association + FIR_CHARACTER, // intrinsic type + FIR_COMPLEX, // intrinsic type + FIR_DERIVED, // derived + FIR_DIMS, + FIR_FIELD, + FIR_HEAP, + FIR_INT, // intrinsic type + FIR_LEN, + FIR_LOGICAL, // intrinsic type + FIR_POINTER, // POINTER attr + FIR_REAL, // intrinsic type + FIR_REFERENCE, + FIR_SEQUENCE, // DIMENSION attr + FIR_TYPEDESC, +}; + +/// Is `t` any of the FIR dialect types? +bool isa_fir_type(mlir::Type t); + +/// Is `t` any of the Standard dialect types? +bool isa_std_type(mlir::Type t); + +/// Is `t` any of the FIR dialect or Standard dialect types? +bool isa_fir_or_std_type(mlir::Type t); + +/// Is `t` a FIR dialect type that implies a memory (de)reference? +bool isaMemref(mlir::Type t); + +/// Is `t` a FIR dialect aggregate type? +bool isanAggregate(mlir::Type t); + +/// Extract the `Type` pointed to from a FIR memory reference type. If `t` is +/// not a memory reference type, then returns a null `Type`. +mlir::Type dyn_cast_ptrEleTy(mlir::Type t); + +/// Boilerplate mixin template +template +struct IntrinsicTypeMixin { + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { return Id; } +}; + +class CharacterType + : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static CharacterType get(mlir::MLIRContext *ctxt, KindTy kind); + KindTy getFKind() const; +}; + +class CplxType : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static CplxType get(mlir::MLIRContext *ctxt, KindTy kind); + KindTy getFKind() const; +}; + +class IntType + : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static IntType get(mlir::MLIRContext *ctxt, KindTy kind); + KindTy getFKind() const; +}; + +class LogicalType + : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static LogicalType get(mlir::MLIRContext *ctxt, KindTy kind); + KindTy getFKind() const; +}; + +class RealType : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static RealType get(mlir::MLIRContext *ctxt, KindTy kind); + KindTy getFKind() const; +}; + +// FIR support types + +class BoxType + : public mlir::Type::TypeBase { +public: + using Base::Base; + static BoxType get(mlir::Type eleTy, mlir::AffineMapAttr map = {}); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_BOX; } + mlir::Type getEleTy() const; + mlir::AffineMapAttr getLayoutMap() const; + + static mlir::LogicalResult + verifyConstructionInvariants(const mlir::AttributeStorage *, mlir::Type eleTy, + mlir::AffineMapAttr map); +}; + +class BoxCharType : public mlir::Type::TypeBase { +public: + using Base::Base; + static BoxCharType get(mlir::MLIRContext *ctxt, KindTy kind); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_BOXCHAR; } + CharacterType getEleTy() const; +}; + +class BoxProcType : public mlir::Type::TypeBase { +public: + using Base::Base; + static BoxProcType get(mlir::Type eleTy); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_BOXPROC; } + mlir::Type getEleTy() const; + + static mlir::LogicalResult + verifyConstructionInvariants(const mlir::AttributeStorage *, + mlir::Type eleTy); +}; + +class DimsType : public mlir::Type::TypeBase { +public: + using Base::Base; + static DimsType get(mlir::MLIRContext *ctx, unsigned rank); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_DIMS; } + + /// returns -1 if the rank is unknown + int getRank() const; +}; + +class FieldType : public mlir::Type::TypeBase { +public: + using Base::Base; + static FieldType get(mlir::MLIRContext *ctxt, KindTy _ = 0); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_FIELD; } +}; + +class HeapType : public mlir::Type::TypeBase { +public: + using Base::Base; + static HeapType get(mlir::Type elementType); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_HEAP; } + + mlir::Type getEleTy() const; + + static mlir::LogicalResult + verifyConstructionInvariants(const mlir::AttributeStorage *, + mlir::Type eleTy); +}; + +class LenType + : public mlir::Type::TypeBase { +public: + using Base::Base; + static LenType get(mlir::MLIRContext *ctxt, KindTy _ = 0); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_LEN; } +}; + +class PointerType : public mlir::Type::TypeBase { +public: + using Base::Base; + static PointerType get(mlir::Type elementType); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_POINTER; } + + mlir::Type getEleTy() const; + + static mlir::LogicalResult + verifyConstructionInvariants(const mlir::AttributeStorage *, + mlir::Type eleTy); +}; + +class ReferenceType + : public mlir::Type::TypeBase { +public: + using Base::Base; + static ReferenceType get(mlir::Type elementType); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_REFERENCE; } + + mlir::Type getEleTy() const; + + static mlir::LogicalResult + verifyConstructionInvariants(const mlir::AttributeStorage *, + mlir::Type eleTy); +}; + +/// A sequence type is a multi-dimensional array of values. The sequence type +/// may have an unknown number of dimensions or the extent of dimensions may be +/// unknown. A sequence type models a Fortran array entity, giving it a type in +/// FIR. A sequence type is assumed to be stored in a column-major order, which +/// differs from LLVM IR and other dialects of MLIR. +class SequenceType : public mlir::Type::TypeBase { +public: + using Base::Base; + using Extent = int64_t; + using Shape = llvm::SmallVector; + + /// Return a sequence type with the specified shape and element type + static SequenceType get(const Shape &shape, mlir::Type elementType, + mlir::AffineMapAttr map = {}); + + /// The element type of this sequence + mlir::Type getEleTy() const; + + /// The shape of the sequence. If the sequence has an unknown shape, the shape + /// returned will be empty. + Shape getShape() const; + + mlir::AffineMapAttr getLayoutMap() const; + + /// The number of dimensions of the sequence + unsigned getDimension() const { return getShape().size(); } + + /// The value `-1` represents an unknown extent for a dimension + static constexpr Extent getUnknownExtent() { return -1; } + + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_SEQUENCE; } + + static mlir::LogicalResult + verifyConstructionInvariants(const mlir::AttributeStorage *attributeStorage, + const Shape &shape, mlir::Type eleTy, + mlir::AffineMapAttr map); +}; + +bool operator==(const SequenceType::Shape &, const SequenceType::Shape &); +llvm::hash_code hash_value(const SequenceType::Extent &); +llvm::hash_code hash_value(const SequenceType::Shape &); + +class TypeDescType : public mlir::Type::TypeBase { +public: + using Base::Base; + static TypeDescType get(mlir::Type ofType); + static constexpr bool kindof(unsigned kind) { + return kind == TypeKind::FIR_TYPEDESC; + } + mlir::Type getOfTy() const; + + static mlir::LogicalResult + verifyConstructionInvariants(const mlir::AttributeStorage *, + mlir::Type ofType); +}; + +// Derived types + +class RecordType : public mlir::Type::TypeBase { +public: + using Base::Base; + using TypePair = std::pair; + using TypeList = std::vector; + + llvm::StringRef getName(); + TypeList getTypeList(); + TypeList getLenParamList(); + + mlir::Type getType(llvm::StringRef ident); + mlir::Type getType(unsigned index) { + assert(index < getNumFields()); + return getTypeList()[index].second; + } + unsigned getNumFields() { return getTypeList().size(); } + unsigned getNumLenParams() { return getLenParamList().size(); } + + static RecordType get(mlir::MLIRContext *ctxt, llvm::StringRef name); + void finalize(llvm::ArrayRef lenPList, + llvm::ArrayRef typeList); + static constexpr bool kindof(unsigned kind) { return kind == getId(); } + static constexpr unsigned getId() { return TypeKind::FIR_DERIVED; } + + detail::RecordTypeStorage const *uniqueKey() const; + + static mlir::LogicalResult + verifyConstructionInvariants(const mlir::AttributeStorage *, + llvm::StringRef name); +}; + +mlir::Type parseFirType(FIROpsDialect *, mlir::DialectAsmParser &parser); + +void printFirType(FIROpsDialect *, mlir::Type ty, mlir::DialectAsmPrinter &p); + +} // namespace fir + +#endif // OPTIMIZER_DIALECT_FIRTYPE_H diff --git a/include/flang/Optimizer/Support/KindMapping.h b/include/flang/Optimizer/Support/KindMapping.h new file mode 100644 index 000000000000..a549a7ed3df2 --- /dev/null +++ b/include/flang/Optimizer/Support/KindMapping.h @@ -0,0 +1,89 @@ +//===-- optimizer/Support/KindMapping.h -------------------------*- 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 OPTIMIZER_SUPPORT_KINDMAPPING_H +#define OPTIMIZER_SUPPORT_KINDMAPPING_H + +#include "llvm/IR/Type.h" +#include + +namespace llvm { +template +class Optional; +struct fltSemantics; +} // namespace llvm + +namespace mlir { +class MLIRContext; +} // namespace mlir + +namespace fir { + +/// The kind mapping is an encoded string that informs FIR how the Fortran KIND +/// values from the front-end should be converted to LLVM IR types. This +/// encoding allows the mapping from front-end KIND values to backend LLVM IR +/// types to be customized by the front-end. +/// +/// The provided string uses the following syntax. +/// +/// intrinsic-key `:` kind-value (`,` intrinsic-key `:` kind-value)* +/// +/// intrinsic-key is a single character for the intrinsic type. +/// 'i' : INTEGER (size in bits) +/// 'l' : LOGICAL (size in bits) +/// 'a' : CHARACTER (size in bits) +/// 'r' : REAL (encoding value) +/// 'c' : COMPLEX (encoding value) +/// +/// kind-value is either an unsigned integer (for 'i', 'l', and 'a') or one of +/// 'Half', 'Float', 'Double', 'X86_FP80', or 'FP128' (for 'r' and 'c'). +/// +/// If LLVM adds support for new floating-point types, the final list should be +/// extended. +class KindMapping { +public: + using KindTy = unsigned; + using Bitsize = unsigned; + using LLVMTypeID = llvm::Type::TypeID; + using MatchResult = llvm::Optional; + + explicit KindMapping(mlir::MLIRContext *context); + explicit KindMapping(mlir::MLIRContext *context, llvm::StringRef map); + + /// Get the size in bits of !fir.char + Bitsize getCharacterBitsize(KindTy kind); + + /// Get the size in bits of !fir.int + Bitsize getIntegerBitsize(KindTy kind); + + /// Get the size in bits of !fir.logical + Bitsize getLogicalBitsize(KindTy kind); + + /// Get the LLVM Type::TypeID of !fir.real + LLVMTypeID getRealTypeID(KindTy kind); + + /// Get the LLVM Type::TypeID of !fir.complex + LLVMTypeID getComplexTypeID(KindTy kind); + + mlir::MLIRContext *getContext() const { return context; } + + /// Get the float semantics of !fir.real + const llvm::fltSemantics &getFloatSemantics(KindTy kind); + +private: + MatchResult badMapString(llvm::Twine const &ptr); + MatchResult parse(llvm::StringRef kindMap); + + mlir::MLIRContext *context; + std::map> intMap; + std::map> floatMap; +}; + +} // namespace fir + +#endif // OPTIMIZER_SUPPORT_KINDMAPPING_H diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index fae2eed92fa8..f2fe30dca5a1 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -12,3 +12,7 @@ add_subdirectory(Decimal) add_subdirectory(Lower) add_subdirectory(Parser) add_subdirectory(Semantics) + +if(LINK_WITH_FIR) + add_subdirectory(Optimizer) +endif() diff --git a/lib/Fir/.clang-format b/lib/Fir/.clang-format deleted file mode 100644 index a74fda4b6734..000000000000 --- a/lib/Fir/.clang-format +++ /dev/null @@ -1,2 +0,0 @@ -BasedOnStyle: LLVM -AlwaysBreakTemplateDeclarations: Yes diff --git a/lib/Optimizer/CMakeLists.txt b/lib/Optimizer/CMakeLists.txt new file mode 100644 index 000000000000..2f8cd269dd9f --- /dev/null +++ b/lib/Optimizer/CMakeLists.txt @@ -0,0 +1,5 @@ +# Sources generated by tablegen have unused parameters. +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") + +add_subdirectory(Dialect) +add_subdirectory(Support) diff --git a/lib/Optimizer/Dialect/CMakeLists.txt b/lib/Optimizer/Dialect/CMakeLists.txt new file mode 100644 index 000000000000..711fc64c5a2a --- /dev/null +++ b/lib/Optimizer/Dialect/CMakeLists.txt @@ -0,0 +1,27 @@ +add_llvm_library(FIRDialect + FIRAttr.cpp + FIRDialect.cpp + FIROps.cpp + FIRType.cpp +) + +add_dependencies(FIRDialect FIROpsIncGen) + +target_link_libraries(FIRDialect + MLIRTargetLLVMIR + MLIRTargetLLVMIRModuleTranslation + MLIREDSC + MLIRExecutionEngine + MLIRParser + MLIRSupport + MLIRStandardToLLVM + MLIRTransforms + LLVMAsmParser + LLVMAsmPrinter + LLVMRemarks +) + +install (TARGETS FIRDialect + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) diff --git a/lib/Optimizer/Dialect/FIRAttr.cpp b/lib/Optimizer/Dialect/FIRAttr.cpp new file mode 100644 index 000000000000..c7695de1d5aa --- /dev/null +++ b/lib/Optimizer/Dialect/FIRAttr.cpp @@ -0,0 +1,236 @@ +//===-- FIRAttr.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/Dialect/FIRAttr.h" +#include "flang/Optimizer/Dialect/FIRDialect.h" +#include "flang/Optimizer/Dialect/FIRType.h" +#include "flang/Optimizer/Support/KindMapping.h" +#include "mlir/IR/AttributeSupport.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/DialectImplementation.h" +#include "mlir/IR/Types.h" +#include "mlir/Parser.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" + +using namespace fir; + +namespace fir { +namespace detail { + +struct RealAttributeStorage : public mlir::AttributeStorage { + using KeyTy = std::pair; + + RealAttributeStorage(int kind, const llvm::APFloat &value) + : kind(kind), value(value) {} + RealAttributeStorage(const KeyTy &key) + : RealAttributeStorage(key.first, key.second) {} + + static unsigned hashKey(const KeyTy &key) { + auto hashVal = llvm::hash_combine(key.first); + return llvm::hash_combine(hashVal, key.second); + } + + bool operator==(const KeyTy &key) const { + return key.first == kind && + key.second.compare(value) == llvm::APFloatBase::cmpEqual; + } + + static RealAttributeStorage * + construct(mlir::AttributeStorageAllocator &allocator, const KeyTy &key) { + return new (allocator.allocate()) + RealAttributeStorage(key); + } + + int getFKind() const { return kind; } + llvm::APFloat getValue() const { return value; } + +private: + int kind; + llvm::APFloat value; +}; + +/// An attribute representing a reference to a type. +struct TypeAttributeStorage : public mlir::AttributeStorage { + using KeyTy = mlir::Type; + + TypeAttributeStorage(mlir::Type value) : value(value) { + assert(value && "must not be of Type null"); + } + + /// Key equality function. + bool operator==(const KeyTy &key) const { return key == value; } + + /// Construct a new storage instance. + static TypeAttributeStorage * + construct(mlir::AttributeStorageAllocator &allocator, KeyTy key) { + return new (allocator.allocate()) + TypeAttributeStorage(key); + } + + mlir::Type getType() const { return value; } + +private: + mlir::Type value; +}; +} // namespace detail + +ExactTypeAttr ExactTypeAttr::get(mlir::Type value) { + return Base::get(value.getContext(), FIR_EXACTTYPE, value); +} + +mlir::Type ExactTypeAttr::getType() const { return getImpl()->getType(); } + +SubclassAttr SubclassAttr::get(mlir::Type value) { + return Base::get(value.getContext(), FIR_SUBCLASS, value); +} + +mlir::Type SubclassAttr::getType() const { return getImpl()->getType(); } + +using AttributeUniquer = mlir::detail::AttributeUniquer; + +ClosedIntervalAttr ClosedIntervalAttr::get(mlir::MLIRContext *ctxt) { + return AttributeUniquer::get(ctxt, getId()); +} + +UpperBoundAttr UpperBoundAttr::get(mlir::MLIRContext *ctxt) { + return AttributeUniquer::get(ctxt, getId()); +} + +LowerBoundAttr LowerBoundAttr::get(mlir::MLIRContext *ctxt) { + return AttributeUniquer::get(ctxt, getId()); +} + +PointIntervalAttr PointIntervalAttr::get(mlir::MLIRContext *ctxt) { + return AttributeUniquer::get(ctxt, getId()); +} + +// RealAttr + +RealAttr RealAttr::get(mlir::MLIRContext *ctxt, + const RealAttr::ValueType &key) { + return Base::get(ctxt, getId(), key); +} + +int RealAttr::getFKind() const { return getImpl()->getFKind(); } + +llvm::APFloat RealAttr::getValue() const { return getImpl()->getValue(); } + +// FIR attribute parsing + +namespace { +mlir::Attribute parseFirRealAttr(FIROpsDialect *dialect, + mlir::DialectAsmParser &parser, + mlir::Type type) { + int kind; + if (parser.parseLess() || parser.parseInteger(kind) || parser.parseComma()) { + parser.emitError(parser.getNameLoc(), "expected '<' kind ','"); + return {}; + } + KindMapping kindMap(dialect->getContext()); + llvm::APFloat value(0.); + if (parser.parseOptionalKeyword("i")) { + // `i` not present, so literal float must be present + double dontCare; + if (parser.parseFloat(dontCare) || parser.parseGreater()) { + parser.emitError(parser.getNameLoc(), "expected real constant '>'"); + return {}; + } + auto fltStr = parser.getFullSymbolSpec() + .drop_until([](char c) { return c == ','; }) + .drop_front() + .drop_while([](char c) { return c == ' ' || c == '\t'; }) + .take_until([](char c) { + return c == '>' || c == ' ' || c == '\t'; + }); + value = llvm::APFloat(kindMap.getFloatSemantics(kind), fltStr); + } else { + // `i` is present, so literal bitstring (hex) must be present + llvm::StringRef hex; + if (parser.parseKeyword(&hex) || parser.parseGreater()) { + parser.emitError(parser.getNameLoc(), "expected real constant '>'"); + return {}; + } + auto bits = llvm::APInt(kind * 8, hex.drop_front(), 16); + value = llvm::APFloat(kindMap.getFloatSemantics(kind), bits); + } + return RealAttr::get(dialect->getContext(), {kind, value}); +} +} // namespace + +mlir::Attribute parseFirAttribute(FIROpsDialect *dialect, + mlir::DialectAsmParser &parser, + mlir::Type type) { + auto loc = parser.getNameLoc(); + llvm::StringRef attrName; + if (parser.parseKeyword(&attrName)) { + parser.emitError(loc, "expected an attribute name"); + return {}; + } + + if (attrName == ExactTypeAttr::getAttrName()) { + mlir::Type type; + if (parser.parseLess() || parser.parseType(type) || parser.parseGreater()) + parser.emitError(loc, "expected a type"); + return ExactTypeAttr::get(type); + } + if (attrName == SubclassAttr::getAttrName()) { + mlir::Type type; + if (parser.parseLess() || parser.parseType(type) || parser.parseGreater()) + parser.emitError(loc, "expected a subtype"); + return SubclassAttr::get(type); + } + if (attrName == PointIntervalAttr::getAttrName()) + return PointIntervalAttr::get(dialect->getContext()); + if (attrName == LowerBoundAttr::getAttrName()) + return LowerBoundAttr::get(dialect->getContext()); + if (attrName == UpperBoundAttr::getAttrName()) + return UpperBoundAttr::get(dialect->getContext()); + if (attrName == ClosedIntervalAttr::getAttrName()) + return ClosedIntervalAttr::get(dialect->getContext()); + if (attrName == RealAttr::getAttrName()) + return parseFirRealAttr(dialect, parser, type); + + llvm::Twine msg{"unknown FIR attribute: "}; + parser.emitError(loc, msg.concat(attrName)); + return {}; +} + +// FIR attribute pretty printer + +void printFirAttribute(FIROpsDialect *dialect, mlir::Attribute attr, + mlir::DialectAsmPrinter &p) { + auto &os = p.getStream(); + if (auto exact = attr.dyn_cast()) { + os << fir::ExactTypeAttr::getAttrName() << '<'; + p.printType(exact.getType()); + os << '>'; + } else if (auto sub = attr.dyn_cast()) { + os << fir::SubclassAttr::getAttrName() << '<'; + p.printType(sub.getType()); + os << '>'; + } else if (attr.dyn_cast_or_null()) { + os << fir::PointIntervalAttr::getAttrName(); + } else if (attr.dyn_cast_or_null()) { + os << fir::ClosedIntervalAttr::getAttrName(); + } else if (attr.dyn_cast_or_null()) { + os << fir::LowerBoundAttr::getAttrName(); + } else if (attr.dyn_cast_or_null()) { + os << fir::UpperBoundAttr::getAttrName(); + } else if (auto a = attr.dyn_cast_or_null()) { + os << fir::RealAttr::getAttrName() << '<' << a.getFKind() << ", i x"; + llvm::SmallString<40> ss; + a.getValue().bitcastToAPInt().toStringUnsigned(ss, 16); + os << ss << '>'; + } else { + assert(false && "attribute pretty-printer is not implemented"); + } +} + +} // namespace fir diff --git a/lib/Optimizer/Dialect/FIRDialect.cpp b/lib/Optimizer/Dialect/FIRDialect.cpp new file mode 100644 index 000000000000..e6b04072bd0b --- /dev/null +++ b/lib/Optimizer/Dialect/FIRDialect.cpp @@ -0,0 +1,77 @@ +//===-- FIRDialect.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/Dialect/FIRDialect.h" +#include "flang/Optimizer/Dialect/FIRAttr.h" +#include "flang/Optimizer/Dialect/FIROps.h" +#include "flang/Optimizer/Dialect/FIRType.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/IR/StandardTypes.h" +#include "mlir/Transforms/SideEffectsInterface.h" + +using namespace fir; + +namespace { + +template +void selectBuild(mlir::OpBuilder *builder, mlir::OperationState *result, + mlir::Value condition, + llvm::ArrayRef tuples) { + result->addOperands(condition); + for (auto &tup : tuples) { + auto *cond{std::get(tup)}; + result->addOperands(cond); + } + // Note: succs must be added *after* operands + for (auto &tup : tuples) { + auto *block{std::get(tup)}; + assert(block); + auto blkArgs{std::get>(tup)}; + result->addSuccessor(block, blkArgs); + } +} + +} // namespace + +fir::FIROpsDialect::FIROpsDialect(mlir::MLIRContext *ctx) + : mlir::Dialect("fir", ctx) { + addTypes(); + addAttributes(); + addOperations< +#define GET_OP_LIST +#include "flang/Optimizer/Dialect/FIROps.cpp.inc" + >(); +} + +// anchor the class vtable to this compilation unit +fir::FIROpsDialect::~FIROpsDialect() { + // do nothing +} + +mlir::Type fir::FIROpsDialect::parseType(mlir::DialectAsmParser &parser) const { + return parseFirType(const_cast(this), parser); +} + +void fir::FIROpsDialect::printType(mlir::Type ty, + mlir::DialectAsmPrinter &p) const { + return printFirType(const_cast(this), ty, p); +} + +mlir::Attribute +fir::FIROpsDialect::parseAttribute(mlir::DialectAsmParser &parser, + mlir::Type type) const { + return parseFirAttribute(const_cast(this), parser, type); +} + +void fir::FIROpsDialect::printAttribute(mlir::Attribute attr, + mlir::DialectAsmPrinter &p) const { + printFirAttribute(const_cast(this), attr, p); +} diff --git a/lib/Optimizer/Dialect/FIROps.cpp b/lib/Optimizer/Dialect/FIROps.cpp new file mode 100644 index 000000000000..aa3bbcfd5942 --- /dev/null +++ b/lib/Optimizer/Dialect/FIROps.cpp @@ -0,0 +1,573 @@ +//===-- FIROps.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/Dialect/FIROps.h" +#include "flang/Optimizer/Dialect/FIRAttr.h" +#include "flang/Optimizer/Dialect/FIROpsSupport.h" +#include "flang/Optimizer/Dialect/FIRType.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/Function.h" +#include "mlir/IR/Module.h" +#include "mlir/IR/StandardTypes.h" +#include "mlir/IR/SymbolTable.h" +#include "llvm/ADT/StringSwitch.h" + +namespace fir { + +/// return true if the sequence type is abstract or the record type is malformed +/// or contains an abstract sequence type +static bool verifyInType(mlir::Type inType, + llvm::SmallVectorImpl &visited) { + if (auto st = inType.dyn_cast()) { + auto shape = st.getShape(); + if (shape.size() == 0) + return true; + for (auto ext : shape) + if (ext < 0) + return true; + } else if (auto rt = inType.dyn_cast()) { + // don't recurse if we're already visiting this one + for (auto name : visited) + if (name == rt.getName()) + return false; + // keep track of record types currently being visited + visited.push_back(rt.getName()); + for (auto &field : rt.getTypeList()) + if (verifyInType(field.second, visited)) + return true; + visited.pop_back(); + } else if (auto rt = inType.dyn_cast()) { + return verifyInType(rt.getEleTy(), visited); + } + return false; +} + +static bool verifyRecordLenParams(mlir::Type inType, unsigned numLenParams) { + if (numLenParams > 0) { + if (auto rt = inType.dyn_cast()) + return numLenParams != rt.getNumLenParams(); + return true; + } + return false; +} + +// AllocaOp + +mlir::Type AllocaOp::getAllocatedType() { + return getType().cast().getEleTy(); +} + +/// Create a legal memory reference as return type +mlir::Type AllocaOp::wrapResultType(mlir::Type intype) { + // FIR semantics: memory references to memory references are disallowed + if (intype.dyn_cast()) + return {}; + return ReferenceType::get(intype); +} + +mlir::Type AllocaOp::getRefTy(mlir::Type ty) { return ReferenceType::get(ty); } + +// AllocMemOp + +mlir::Type AllocMemOp::getAllocatedType() { + return getType().cast().getEleTy(); +} + +mlir::Type AllocMemOp::getRefTy(mlir::Type ty) { return HeapType::get(ty); } + +/// Create a legal heap reference as return type +mlir::Type AllocMemOp::wrapResultType(mlir::Type intype) { + // Fortran semantics: C852 an entity cannot be both ALLOCATABLE and POINTER + // 8.5.3 note 1 prohibits ALLOCATABLE procedures as well + // FIR semantics: one may not allocate a memory reference value + if (intype.dyn_cast() || intype.dyn_cast() || + intype.dyn_cast() || intype.dyn_cast()) + return {}; + return HeapType::get(intype); +} + +// BoxDimsOp + +/// Get the result types packed in a tuple tuple +mlir::Type BoxDimsOp::getTupleType() { + llvm::SmallVector triple{ + getResult(0).getType(), getResult(1).getType(), getResult(2).getType()}; + return mlir::TupleType::get(triple, getContext()); +} + +// CallOp + +void printCallOp(mlir::OpAsmPrinter &p, fir::CallOp &op) { + auto callee = op.callee(); + bool isDirect = callee.hasValue(); + p << op.getOperationName() << ' '; + if (isDirect) + p << callee.getValue(); + else + p << op.getOperand(0); + p << '('; + p.printOperands(llvm::drop_begin(op.getOperands(), isDirect ? 0 : 1)); + p << ')'; + p.printOptionalAttrDict(op.getAttrs(), {"callee"}); + auto resultTypes{op.getResultTypes()}; + llvm::SmallVector argTypes( + llvm::drop_begin(op.getOperandTypes(), isDirect ? 0 : 1)); + p << " : " << FunctionType::get(argTypes, resultTypes, op.getContext()); +} + +mlir::ParseResult parseCallOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + llvm::SmallVector operands; + + if (parser.parseOperandList(operands)) + return mlir::failure(); + bool isDirect = operands.empty(); + SmallVector attrs; + SymbolRefAttr funcAttr; + + if (isDirect) + if (parser.parseAttribute(funcAttr, "callee", attrs)) + return mlir::failure(); + Type type; + + if (parser.parseOperandList(operands, mlir::OpAsmParser::Delimiter::Paren) || + parser.parseOptionalAttrDict(attrs) || parser.parseColon() || + parser.parseType(type)) + return mlir::failure(); + + auto funcType = type.dyn_cast(); + if (!funcType) + return parser.emitError(parser.getNameLoc(), "expected function type"); + if (isDirect) { + if (parser.resolveOperands(operands, funcType.getInputs(), + parser.getNameLoc(), result.operands)) + return mlir::failure(); + } else { + auto funcArgs = + llvm::ArrayRef(operands).drop_front(); + llvm::SmallVector resultArgs( + result.operands.begin() + (result.operands.empty() ? 0 : 1), + result.operands.end()); + if (parser.resolveOperand(operands[0], funcType, result.operands) || + parser.resolveOperands(funcArgs, funcType.getInputs(), + parser.getNameLoc(), resultArgs)) + return mlir::failure(); + } + result.addTypes(funcType.getResults()); + result.attributes = attrs; + return mlir::success(); +} + +// CmpfOp + +fir::CmpFPredicate CmpfOp::getPredicateByName(llvm::StringRef name) { + return llvm::StringSwitch(name) + .Case("false", CmpFPredicate::AlwaysFalse) + .Case("oeq", CmpFPredicate::OEQ) + .Case("ogt", CmpFPredicate::OGT) + .Case("oge", CmpFPredicate::OGE) + .Case("olt", CmpFPredicate::OLT) + .Case("ole", CmpFPredicate::OLE) + .Case("one", CmpFPredicate::ONE) + .Case("ord", CmpFPredicate::ORD) + .Case("ueq", CmpFPredicate::UEQ) + .Case("ugt", CmpFPredicate::UGT) + .Case("uge", CmpFPredicate::UGE) + .Case("ult", CmpFPredicate::ULT) + .Case("ule", CmpFPredicate::ULE) + .Case("une", CmpFPredicate::UNE) + .Case("uno", CmpFPredicate::UNO) + .Case("true", CmpFPredicate::AlwaysTrue) + .Default(CmpFPredicate::NumPredicates); +} + +void buildCmpFOp(Builder *builder, OperationState &result, + CmpFPredicate predicate, Value lhs, Value rhs) { + result.addOperands({lhs, rhs}); + result.types.push_back(builder->getI1Type()); + result.addAttribute( + CmpfOp::getPredicateAttrName(), + builder->getI64IntegerAttr(static_cast(predicate))); +} + +template +void printCmpOp(OpAsmPrinter &p, OPTY op) { + static const char *predicateNames[] = { + /*AlwaysFalse*/ "false", + /*OEQ*/ "oeq", + /*OGT*/ "ogt", + /*OGE*/ "oge", + /*OLT*/ "olt", + /*OLE*/ "ole", + /*ONE*/ "one", + /*ORD*/ "ord", + /*UEQ*/ "ueq", + /*UGT*/ "ugt", + /*UGE*/ "uge", + /*ULT*/ "ult", + /*ULE*/ "ule", + /*UNE*/ "une", + /*UNO*/ "uno", + /*AlwaysTrue*/ "true", + }; + static_assert(std::extent::value == + (size_t)fir::CmpFPredicate::NumPredicates, + "wrong number of predicate names"); + p << op.getOperationName() << ' '; + auto predicateValue = + op.template getAttrOfType(OPTY::getPredicateAttrName()) + .getInt(); + assert(predicateValue >= static_cast(CmpFPredicate::FirstValidValue) && + predicateValue < static_cast(CmpFPredicate::NumPredicates) && + "unknown predicate index"); + Builder b(op.getContext()); + auto predicateStringAttr = b.getStringAttr(predicateNames[predicateValue]); + p.printAttribute(predicateStringAttr); + p << ", "; + p.printOperand(op.lhs()); + p << ", "; + p.printOperand(op.rhs()); + p.printOptionalAttrDict(op.getAttrs(), + /*elidedAttrs=*/{OPTY::getPredicateAttrName()}); + p << " : " << op.lhs().getType(); +} + +void printCmpfOp(OpAsmPrinter &p, CmpfOp op) { printCmpOp(p, op); } + +template +mlir::ParseResult parseCmpOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + llvm::SmallVector ops; + llvm::SmallVector attrs; + mlir::Attribute predicateNameAttr; + mlir::Type type; + if (parser.parseAttribute(predicateNameAttr, OPTY::getPredicateAttrName(), + attrs) || + parser.parseComma() || parser.parseOperandList(ops, 2) || + parser.parseOptionalAttrDict(attrs) || parser.parseColonType(type) || + parser.resolveOperands(ops, type, result.operands)) + return failure(); + + if (!predicateNameAttr.isa()) + return parser.emitError(parser.getNameLoc(), + "expected string comparison predicate attribute"); + + // Rewrite string attribute to an enum value. + llvm::StringRef predicateName = + predicateNameAttr.cast().getValue(); + auto predicate = CmpfOp::getPredicateByName(predicateName); + if (predicate == CmpFPredicate::NumPredicates) + return parser.emitError(parser.getNameLoc(), + "unknown comparison predicate \"" + predicateName + + "\""); + + auto builder = parser.getBuilder(); + mlir::Type i1Type = builder.getI1Type(); + attrs[0].second = builder.getI64IntegerAttr(static_cast(predicate)); + result.attributes = attrs; + result.addTypes({i1Type}); + return success(); +} + +mlir::ParseResult parseCmpfOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + return parseCmpOp(parser, result); +} + +// CmpcOp + +void buildCmpCOp(Builder *builder, OperationState &result, + CmpFPredicate predicate, Value lhs, Value rhs) { + result.addOperands({lhs, rhs}); + result.types.push_back(builder->getI1Type()); + result.addAttribute( + CmpcOp::getPredicateAttrName(), + builder->getI64IntegerAttr(static_cast(predicate))); +} + +void printCmpcOp(OpAsmPrinter &p, CmpcOp op) { printCmpOp(p, op); } + +mlir::ParseResult parseCmpcOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + return parseCmpOp(parser, result); +} + +// DispatchOp + +mlir::FunctionType DispatchOp::getFunctionType() { + auto attr = getAttr("fn_type").cast(); + return attr.getValue().cast(); +} + +// DispatchTableOp + +void DispatchTableOp::appendTableEntry(mlir::Operation *op) { + assert(mlir::isa(*op) && "operation must be a DTEntryOp"); + auto &block = getBlock(); + block.getOperations().insert(block.end(), op); +} + +// GenTypeDescOp + +void GenTypeDescOp::build(Builder *, OperationState &result, + mlir::TypeAttr inty) { + result.addAttribute("in_type", inty); + result.addTypes(TypeDescType::get(inty.getValue())); +} + +// GlobalOp + +void GlobalOp::appendInitialValue(mlir::Operation *op) { + auto &block = getBlock(); + block.getOperations().insert(block.end(), op); +} + +// LoadOp + +/// Get the element type of a reference like type; otherwise null +mlir::Type elementTypeOf(mlir::Type ref) { + if (auto r = ref.dyn_cast_or_null()) + return r.getEleTy(); + if (auto r = ref.dyn_cast_or_null()) + return r.getEleTy(); + if (auto r = ref.dyn_cast_or_null()) + return r.getEleTy(); + return {}; +} + +mlir::ParseResult LoadOp::getElementOf(mlir::Type &ele, mlir::Type ref) { + if (mlir::Type r = elementTypeOf(ref)) { + ele = r; + return mlir::success(); + } + return mlir::failure(); +} + +// LoopOp + +void LoopOp::build(mlir::Builder *builder, mlir::OperationState &result, + int64_t lowerBound, int64_t upperBound, int64_t step) { + assert(false && "not implemented"); +} + +void LoopOp::build(mlir::Builder *builder, mlir::OperationState &result, + mlir::Value lb, mlir::Value ub, + llvm::ArrayRef step) { + if (step.empty()) + result.addOperands({lb, ub}); + else + result.addOperands({lb, ub, step[0]}); + mlir::Region *bodyRegion = result.addRegion(); + LoopOp::ensureTerminator(*bodyRegion, *builder, result.location); + bodyRegion->front().addArgument(builder->getIndexType()); +} + +mlir::ParseResult parseLoopOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + auto &builder = parser.getBuilder(); + mlir::OpAsmParser::OperandType inductionVariable, lb, ub, step; + // Parse the induction variable followed by '='. + if (parser.parseRegionArgument(inductionVariable) || parser.parseEqual()) + return mlir::failure(); + + // Parse loop bounds. + mlir::Type indexType = builder.getIndexType(); + if (parser.parseOperand(lb) || + parser.resolveOperand(lb, indexType, result.operands) || + parser.parseKeyword("to") || parser.parseOperand(ub) || + parser.resolveOperand(ub, indexType, result.operands)) + return mlir::failure(); + + if (parser.parseOptionalKeyword(LoopOp::stepKeyword())) { + result.addAttribute(LoopOp::stepKeyword(), + builder.getIntegerAttr(builder.getIndexType(), 1)); + } else if (parser.parseOperand(step) || + parser.resolveOperand(step, indexType, result.operands)) { + return mlir::failure(); + } + + // Parse the optional `unordered` keyword + bool isUnordered = false; + if (!parser.parseOptionalKeyword(LoopOp::unorderedKeyword())) { + result.addAttribute(LoopOp::unorderedKeyword(), builder.getUnitAttr()); + isUnordered = true; + } + + // Parse the body region. + mlir::Region *body = result.addRegion(); + if (parser.parseRegion(*body, inductionVariable, indexType)) + return mlir::failure(); + + fir::LoopOp::ensureTerminator(*body, builder, result.location); + + // Parse the optional attribute list. + if (parser.parseOptionalAttrDict(result.attributes)) + return mlir::failure(); + if (!isUnordered) + result.addTypes(builder.getIndexType()); + return mlir::success(); +} + +fir::LoopOp getForInductionVarOwner(mlir::Value val) { + auto ivArg = val.dyn_cast(); + if (!ivArg) + return fir::LoopOp(); + assert(ivArg.getOwner() && "unlinked block argument"); + auto *containingInst = ivArg.getOwner()->getParentOp(); + return dyn_cast_or_null(containingInst); +} + +// StoreOp + +mlir::Type StoreOp::elementType(mlir::Type refType) { + if (auto ref = refType.dyn_cast()) + return ref.getEleTy(); + if (auto ref = refType.dyn_cast()) + return ref.getEleTy(); + if (auto ref = refType.dyn_cast()) + return ref.getEleTy(); + return {}; +} + +// StringLitOp + +bool StringLitOp::isWideValue() { + auto eleTy = getType().cast().getEleTy(); + return eleTy.cast().getFKind() != 1; +} + +// WhereOp + +void WhereOp::build(mlir::Builder *builder, mlir::OperationState &result, + mlir::Value cond, bool withElseRegion) { + result.addOperands(cond); + mlir::Region *thenRegion = result.addRegion(); + mlir::Region *elseRegion = result.addRegion(); + WhereOp::ensureTerminator(*thenRegion, *builder, result.location); + if (withElseRegion) + WhereOp::ensureTerminator(*elseRegion, *builder, result.location); +} + +mlir::ParseResult parseWhereOp(mlir::OpAsmParser &parser, + mlir::OperationState &result) { + // Create the regions for 'then'. + result.regions.reserve(2); + mlir::Region *thenRegion = result.addRegion(); + mlir::Region *elseRegion = result.addRegion(); + + auto &builder = parser.getBuilder(); + mlir::OpAsmParser::OperandType cond; + mlir::Type i1Type = builder.getIntegerType(1); + if (parser.parseOperand(cond) || + parser.resolveOperand(cond, i1Type, result.operands)) + return mlir::failure(); + + if (parser.parseRegion(*thenRegion, {}, {})) + return mlir::failure(); + + WhereOp::ensureTerminator(*thenRegion, parser.getBuilder(), result.location); + + if (!parser.parseOptionalKeyword("otherwise")) { + if (parser.parseRegion(*elseRegion, {}, {})) + return mlir::failure(); + WhereOp::ensureTerminator(*elseRegion, parser.getBuilder(), + result.location); + } + + // Parse the optional attribute list. + if (parser.parseOptionalAttrDict(result.attributes)) + return mlir::failure(); + + return mlir::success(); +} + +mlir::ParseResult isValidCaseAttr(mlir::Attribute attr) { + if (attr.dyn_cast_or_null() || + attr.dyn_cast_or_null() || + attr.dyn_cast_or_null() || + attr.dyn_cast_or_null() || + attr.dyn_cast_or_null()) + return mlir::success(); + return mlir::failure(); +} + +unsigned getCaseArgumentOffset(llvm::ArrayRef cases, + unsigned dest) { + unsigned o = 0; + for (unsigned i = 0; i < dest; ++i) { + auto &attr = cases[i]; + if (!attr.dyn_cast_or_null()) { + ++o; + if (attr.dyn_cast_or_null()) + ++o; + } + } + return o; +} + +mlir::ParseResult parseSelector(mlir::OpAsmParser &parser, + mlir::OperationState &result, + mlir::OpAsmParser::OperandType &selector, + mlir::Type &type) { + if (parser.parseOperand(selector) || parser.parseColonType(type) || + parser.resolveOperand(selector, type, result.operands) || + parser.parseLSquare()) + return mlir::failure(); + return mlir::success(); +} + +/// Generic pretty-printer of a binary operation +void printBinaryOp(Operation *op, OpAsmPrinter &p) { + assert(op->getNumOperands() == 2 && "binary op must have two operands"); + assert(op->getNumResults() == 1 && "binary op must have one result"); + + p << op->getName() << ' ' << op->getOperand(0) << ", " << op->getOperand(1); + p.printOptionalAttrDict(op->getAttrs()); + p << " : " << op->getResult(0).getType(); +} + +/// Generic pretty-printer of an unary operation +void printUnaryOp(Operation *op, OpAsmPrinter &p) { + assert(op->getNumOperands() == 1 && "unary op must have one operand"); + assert(op->getNumResults() == 1 && "unary op must have one result"); + + p << op->getName() << ' ' << op->getOperand(0); + p.printOptionalAttrDict(op->getAttrs()); + p << " : " << op->getResult(0).getType(); +} + +bool isReferenceLike(mlir::Type type) { + return type.isa() || type.isa() || + type.isa(); +} + +mlir::FuncOp createFuncOp(mlir::Location loc, mlir::ModuleOp module, + StringRef name, mlir::FunctionType type, + llvm::ArrayRef attrs) { + if (auto f = module.lookupSymbol(name)) + return f; + mlir::OpBuilder modBuilder(module.getBodyRegion()); + return modBuilder.create(loc, name, type, attrs); +} + +GlobalOp createGlobalOp(mlir::Location loc, mlir::ModuleOp module, + StringRef name, mlir::Type type, + llvm::ArrayRef attrs) { + if (auto g = module.lookupSymbol(name)) + return g; + mlir::OpBuilder modBuilder(module.getBodyRegion()); + return modBuilder.create(loc, name, type, attrs); +} + +// Tablegen operators + +#define GET_OP_CLASSES +#include "flang/Optimizer/Dialect/FIROps.cpp.inc" + +} // namespace fir diff --git a/lib/Optimizer/Dialect/FIRType.cpp b/lib/Optimizer/Dialect/FIRType.cpp new file mode 100644 index 000000000000..7fd4547448ff --- /dev/null +++ b/lib/Optimizer/Dialect/FIRType.cpp @@ -0,0 +1,1288 @@ +//===-- FIRType.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/Dialect/FIRType.h" +#include "flang/Optimizer/Dialect/FIRDialect.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/Dialect.h" +#include "mlir/IR/DialectImplementation.h" +#include "mlir/IR/StandardTypes.h" +#include "mlir/Parser.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringSet.h" + +using namespace fir; + +namespace { + +template +TYPE parseIntSingleton(mlir::DialectAsmParser &parser) { + int kind = 0; + if (parser.parseLess() || parser.parseInteger(kind) || + parser.parseGreater()) { + parser.emitError(parser.getCurrentLocation(), "kind value expected"); + return {}; + } + return TYPE::get(parser.getBuilder().getContext(), kind); +} + +template +TYPE parseKindSingleton(mlir::DialectAsmParser &parser) { + return parseIntSingleton(parser); +} + +template +TYPE parseRankSingleton(mlir::DialectAsmParser &parser) { + return parseIntSingleton(parser); +} + +template +TYPE parseTypeSingleton(mlir::DialectAsmParser &parser, mlir::Location) { + mlir::Type ty; + if (parser.parseLess() || parser.parseType(ty) || parser.parseGreater()) { + parser.emitError(parser.getCurrentLocation(), "type expected"); + return {}; + } + return TYPE::get(ty); +} + +// `box` `<` type (',' affine-map)? `>` +BoxType parseBox(mlir::DialectAsmParser &parser, mlir::Location loc) { + mlir::Type ofTy; + if (parser.parseLess() || parser.parseType(ofTy)) { + parser.emitError(parser.getCurrentLocation(), "expected type parameter"); + return {}; + } + + mlir::AffineMapAttr map; + if (!parser.parseOptionalComma()) + if (parser.parseAttribute(map)) { + parser.emitError(parser.getCurrentLocation(), "expected affine map"); + return {}; + } + if (parser.parseGreater()) { + parser.emitError(parser.getCurrentLocation(), "expected '>'"); + return {}; + } + return BoxType::get(ofTy, map); +} + +// `boxchar` `<` kind `>` +BoxCharType parseBoxChar(mlir::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} + +// `boxproc` `<` return-type `>` +BoxProcType parseBoxProc(mlir::DialectAsmParser &parser, mlir::Location loc) { + return parseTypeSingleton(parser, loc); +} + +// `char` `<` kind `>` +CharacterType parseCharacter(mlir::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} + +// `complex` `<` kind `>` +CplxType parseComplex(mlir::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} + +// `dims` `<` rank `>` +DimsType parseDims(mlir::DialectAsmParser &parser) { + return parseRankSingleton(parser); +} + +// `field` +FieldType parseField(mlir::DialectAsmParser &parser) { + return FieldType::get(parser.getBuilder().getContext()); +} + +// `heap` `<` type `>` +HeapType parseHeap(mlir::DialectAsmParser &parser, mlir::Location loc) { + return parseTypeSingleton(parser, loc); +} + +// `int` `<` kind `>` +IntType parseInteger(mlir::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} + +// `len` +LenType parseLen(mlir::DialectAsmParser &parser) { + return LenType::get(parser.getBuilder().getContext()); +} + +// `logical` `<` kind `>` +LogicalType parseLogical(mlir::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} + +// `ptr` `<` type `>` +PointerType parsePointer(mlir::DialectAsmParser &parser, mlir::Location loc) { + return parseTypeSingleton(parser, loc); +} + +// `real` `<` kind `>` +RealType parseReal(mlir::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} + +// `ref` `<` type `>` +ReferenceType parseReference(mlir::DialectAsmParser &parser, + mlir::Location loc) { + return parseTypeSingleton(parser, loc); +} + +// `tdesc` `<` type `>` +TypeDescType parseTypeDesc(mlir::DialectAsmParser &parser, mlir::Location loc) { + return parseTypeSingleton(parser, loc); +} + +// `void` +mlir::Type parseVoid(mlir::DialectAsmParser &parser) { + return parser.getBuilder().getNoneType(); +} + +// `array` `<` `*` | bounds (`x` bounds)* `:` type (',' affine-map)? `>` +// bounds ::= `?` | int-lit +SequenceType parseSequence(mlir::DialectAsmParser &parser, mlir::Location) { + if (parser.parseLess()) { + parser.emitError(parser.getNameLoc(), "expecting '<'"); + return {}; + } + SequenceType::Shape shape; + if (parser.parseOptionalStar()) { + if (parser.parseDimensionList(shape, true)) { + parser.emitError(parser.getNameLoc(), "invalid shape"); + return {}; + } + } else if (parser.parseColon()) { + parser.emitError(parser.getNameLoc(), "expected ':'"); + return {}; + } + mlir::Type eleTy; + if (parser.parseType(eleTy) || parser.parseGreater()) { + parser.emitError(parser.getNameLoc(), "expecting element type"); + return {}; + } + mlir::AffineMapAttr map; + if (!parser.parseOptionalComma()) + if (parser.parseAttribute(map)) { + parser.emitError(parser.getNameLoc(), "expecting affine map"); + return {}; + } + return SequenceType::get(shape, eleTy, map); +} + +bool verifyIntegerType(mlir::Type ty) { + return ty.isa() || ty.isa(); +} + +bool verifyRecordMemberType(mlir::Type ty) { + return !(ty.isa() || ty.isa() || + ty.isa() || ty.isa() || ty.isa() || + ty.isa() || ty.isa() || + ty.isa()); +} + +bool verifySameLists(llvm::ArrayRef a1, + llvm::ArrayRef a2) { + // FIXME: do we need to allow for any variance here? + return a1 == a2; +} + +RecordType verifyDerived(mlir::DialectAsmParser &parser, RecordType derivedTy, + llvm::ArrayRef lenPList, + llvm::ArrayRef typeList) { + auto loc = parser.getNameLoc(); + if (!verifySameLists(derivedTy.getLenParamList(), lenPList) || + !verifySameLists(derivedTy.getTypeList(), typeList)) { + parser.emitError(loc, "cannot redefine record type members"); + return {}; + } + for (auto &p : lenPList) + if (!verifyIntegerType(p.second)) { + parser.emitError(loc, "LEN parameter must be integral type"); + return {}; + } + for (auto &p : typeList) + if (!verifyRecordMemberType(p.second)) { + parser.emitError(loc, "field parameter has invalid type"); + return {}; + } + llvm::StringSet<> uniq; + for (auto &p : lenPList) + if (!uniq.insert(p.first).second) { + parser.emitError(loc, "LEN parameter cannot have duplicate name"); + return {}; + } + for (auto &p : typeList) + if (!uniq.insert(p.first).second) { + parser.emitError(loc, "field cannot have duplicate name"); + return {}; + } + return derivedTy; +} + +// Fortran derived type +// `type` `<` name +// (`(` id `:` type (`,` id `:` type)* `)`)? +// (`{` id `:` type (`,` id `:` type)* `}`)? '>' +RecordType parseDerived(mlir::DialectAsmParser &parser, mlir::Location) { + llvm::StringRef name; + if (parser.parseLess() || parser.parseKeyword(&name)) { + parser.emitError(parser.getNameLoc(), + "expected a identifier as name of derived type"); + return {}; + } + RecordType result = RecordType::get(parser.getBuilder().getContext(), name); + + RecordType::TypeList lenParamList; + if (!parser.parseOptionalLParen()) { + while (true) { + llvm::StringRef lenparam; + mlir::Type intTy; + if (parser.parseKeyword(&lenparam) || parser.parseColon() || + parser.parseType(intTy)) { + parser.emitError(parser.getNameLoc(), "expected LEN parameter list"); + return {}; + } + lenParamList.emplace_back(lenparam, intTy); + if (parser.parseOptionalComma()) + break; + } + if (parser.parseRParen()) { + parser.emitError(parser.getNameLoc(), "expected ')'"); + return {}; + } + } + + RecordType::TypeList typeList; + if (!parser.parseOptionalLBrace()) { + while (true) { + llvm::StringRef field; + mlir::Type fldTy; + if (parser.parseKeyword(&field) || parser.parseColon() || + parser.parseType(fldTy)) { + parser.emitError(parser.getNameLoc(), "expected field type list"); + return {}; + } + typeList.emplace_back(field, fldTy); + if (parser.parseOptionalComma()) + break; + } + if (parser.parseRBrace()) { + parser.emitError(parser.getNameLoc(), "expected '}'"); + return {}; + } + } + + if (parser.parseGreater()) { + parser.emitError(parser.getNameLoc(), "expected '>' in type type"); + return {}; + } + + if (lenParamList.empty() && typeList.empty()) + return result; + + result.finalize(lenParamList, typeList); + return verifyDerived(parser, result, lenParamList, typeList); +} + +// !fir.ptr and !fir.heap where X is !fir.ptr, !fir.heap, or !fir.ref +// is undefined and disallowed. +inline bool singleIndirectionLevel(mlir::Type ty) { + return !fir::isaMemref(ty); +} + +} // namespace + +// Implementation of the thin interface from dialect to type parser + +mlir::Type fir::parseFirType(FIROpsDialect *, mlir::DialectAsmParser &parser) { + llvm::StringRef typeNameLit; + if (mlir::failed(parser.parseKeyword(&typeNameLit))) + return {}; + + auto loc = parser.getEncodedSourceLoc(parser.getNameLoc()); + if (typeNameLit == "array") + return parseSequence(parser, loc); + if (typeNameLit == "box") + return parseBox(parser, loc); + if (typeNameLit == "boxchar") + return parseBoxChar(parser); + if (typeNameLit == "boxproc") + return parseBoxProc(parser, loc); + if (typeNameLit == "char") + return parseCharacter(parser); + if (typeNameLit == "complex") + return parseComplex(parser); + if (typeNameLit == "dims") + return parseDims(parser); + if (typeNameLit == "field") + return parseField(parser); + if (typeNameLit == "heap") + return parseHeap(parser, loc); + if (typeNameLit == "int") + return parseInteger(parser); + if (typeNameLit == "len") + return parseLen(parser); + if (typeNameLit == "logical") + return parseLogical(parser); + if (typeNameLit == "ptr") + return parsePointer(parser, loc); + if (typeNameLit == "real") + return parseReal(parser); + if (typeNameLit == "ref") + return parseReference(parser, loc); + if (typeNameLit == "tdesc") + return parseTypeDesc(parser, loc); + if (typeNameLit == "type") + return parseDerived(parser, loc); + if (typeNameLit == "void") + return parseVoid(parser); + + parser.emitError(parser.getNameLoc(), "unknown FIR type " + typeNameLit); + return {}; +} + +namespace fir { +namespace detail { + +// Type storage classes + +/// `CHARACTER` storage +struct CharacterTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static CharacterTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy kind) { + auto *storage = allocator.allocate(); + return new (storage) CharacterTypeStorage{kind}; + } + + KindTy getFKind() const { return kind; } + +protected: + KindTy kind; + +private: + CharacterTypeStorage() = delete; + explicit CharacterTypeStorage(KindTy kind) : kind{kind} {} +}; + +struct DimsTypeStorage : public mlir::TypeStorage { + using KeyTy = unsigned; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { + return key == static_cast(getRank()); + } + + static DimsTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + int rank) { + auto *storage = allocator.allocate(); + return new (storage) DimsTypeStorage{rank}; + } + + int getRank() const { return rank; } + +protected: + int rank; + +private: + DimsTypeStorage() = delete; + explicit DimsTypeStorage(int rank) : rank{rank} {} +}; + +/// The type of a derived type part reference +struct FieldTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &) { return llvm::hash_combine(0); } + + bool operator==(const KeyTy &) const { return true; } + + static FieldTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy) { + auto *storage = allocator.allocate(); + return new (storage) FieldTypeStorage{0}; + } + +private: + FieldTypeStorage() = delete; + explicit FieldTypeStorage(KindTy) {} +}; + +/// The type of a derived type LEN parameter reference +struct LenTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &) { return llvm::hash_combine(0); } + + bool operator==(const KeyTy &) const { return true; } + + static LenTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy) { + auto *storage = allocator.allocate(); + return new (storage) LenTypeStorage{0}; + } + +private: + LenTypeStorage() = delete; + explicit LenTypeStorage(KindTy) {} +}; + +/// `LOGICAL` storage +struct LogicalTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static LogicalTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy kind) { + auto *storage = allocator.allocate(); + return new (storage) LogicalTypeStorage{kind}; + } + + KindTy getFKind() const { return kind; } + +protected: + KindTy kind; + +private: + LogicalTypeStorage() = delete; + explicit LogicalTypeStorage(KindTy kind) : kind{kind} {} +}; + +/// `INTEGER` storage +struct IntTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static IntTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy kind) { + auto *storage = allocator.allocate(); + return new (storage) IntTypeStorage{kind}; + } + + KindTy getFKind() const { return kind; } + +protected: + KindTy kind; + +private: + IntTypeStorage() = delete; + explicit IntTypeStorage(KindTy kind) : kind{kind} {} +}; + +/// `COMPLEX` storage +struct CplxTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static CplxTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy kind) { + auto *storage = allocator.allocate(); + return new (storage) CplxTypeStorage{kind}; + } + + KindTy getFKind() const { return kind; } + +protected: + KindTy kind; + +private: + CplxTypeStorage() = delete; + explicit CplxTypeStorage(KindTy kind) : kind{kind} {} +}; + +/// `REAL` storage (for reals of unsupported sizes) +struct RealTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static RealTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy kind) { + auto *storage = allocator.allocate(); + return new (storage) RealTypeStorage{kind}; + } + + KindTy getFKind() const { return kind; } + +protected: + KindTy kind; + +private: + RealTypeStorage() = delete; + explicit RealTypeStorage(KindTy kind) : kind{kind} {} +}; + +/// Boxed object (a Fortran descriptor) +struct BoxTypeStorage : public mlir::TypeStorage { + using KeyTy = std::tuple; + + static unsigned hashKey(const KeyTy &key) { + auto hashVal{llvm::hash_combine(std::get(key))}; + return llvm::hash_combine( + hashVal, llvm::hash_combine(std::get(key))); + } + + bool operator==(const KeyTy &key) const { + return std::get(key) == getElementType() && + std::get(key) == getLayoutMap(); + } + + static BoxTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + const KeyTy &key) { + auto *storage = allocator.allocate(); + return new (storage) BoxTypeStorage{std::get(key), + std::get(key)}; + } + + mlir::Type getElementType() const { return eleTy; } + mlir::AffineMapAttr getLayoutMap() const { return map; } + +protected: + mlir::Type eleTy; + mlir::AffineMapAttr map; + +private: + BoxTypeStorage() = delete; + explicit BoxTypeStorage(mlir::Type eleTy, mlir::AffineMapAttr map) + : eleTy{eleTy}, map{map} {} +}; + +/// Boxed CHARACTER object type +struct BoxCharTypeStorage : public mlir::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static BoxCharTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + KindTy kind) { + auto *storage = allocator.allocate(); + return new (storage) BoxCharTypeStorage{kind}; + } + + KindTy getFKind() const { return kind; } + + // a !fir.boxchar always wraps a !fir.char + CharacterType getElementType(mlir::MLIRContext *ctxt) const { + return CharacterType::get(ctxt, getFKind()); + } + +protected: + KindTy kind; + +private: + BoxCharTypeStorage() = delete; + explicit BoxCharTypeStorage(KindTy kind) : kind{kind} {} +}; + +/// Boxed PROCEDURE POINTER object type +struct BoxProcTypeStorage : public mlir::TypeStorage { + using KeyTy = mlir::Type; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getElementType(); } + + static BoxProcTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + mlir::Type eleTy) { + assert(eleTy && "element type is null"); + auto *storage = allocator.allocate(); + return new (storage) BoxProcTypeStorage{eleTy}; + } + + mlir::Type getElementType() const { return eleTy; } + +protected: + mlir::Type eleTy; + +private: + BoxProcTypeStorage() = delete; + explicit BoxProcTypeStorage(mlir::Type eleTy) : eleTy{eleTy} {} +}; + +/// Pointer-like object storage +struct ReferenceTypeStorage : public mlir::TypeStorage { + using KeyTy = mlir::Type; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getElementType(); } + + static ReferenceTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + mlir::Type eleTy) { + assert(eleTy && "element type is null"); + auto *storage = allocator.allocate(); + return new (storage) ReferenceTypeStorage{eleTy}; + } + + mlir::Type getElementType() const { return eleTy; } + +protected: + mlir::Type eleTy; + +private: + ReferenceTypeStorage() = delete; + explicit ReferenceTypeStorage(mlir::Type eleTy) : eleTy{eleTy} {} +}; + +/// Pointer object storage +struct PointerTypeStorage : public mlir::TypeStorage { + using KeyTy = mlir::Type; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getElementType(); } + + static PointerTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + mlir::Type eleTy) { + assert(eleTy && "element type is null"); + auto *storage = allocator.allocate(); + return new (storage) PointerTypeStorage{eleTy}; + } + + mlir::Type getElementType() const { return eleTy; } + +protected: + mlir::Type eleTy; + +private: + PointerTypeStorage() = delete; + explicit PointerTypeStorage(mlir::Type eleTy) : eleTy{eleTy} {} +}; + +/// Heap memory reference object storage +struct HeapTypeStorage : public mlir::TypeStorage { + using KeyTy = mlir::Type; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getElementType(); } + + static HeapTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + mlir::Type eleTy) { + assert(eleTy && "element type is null"); + auto *storage = allocator.allocate(); + return new (storage) HeapTypeStorage{eleTy}; + } + + mlir::Type getElementType() const { return eleTy; } + +protected: + mlir::Type eleTy; + +private: + HeapTypeStorage() = delete; + explicit HeapTypeStorage(mlir::Type eleTy) : eleTy{eleTy} {} +}; + +/// Sequence-like object storage +struct SequenceTypeStorage : public mlir::TypeStorage { + using KeyTy = + std::tuple; + + static unsigned hashKey(const KeyTy &key) { + auto shapeHash{hash_value(std::get(key))}; + shapeHash = llvm::hash_combine(shapeHash, std::get(key)); + return llvm::hash_combine(shapeHash, std::get(key)); + } + + bool operator==(const KeyTy &key) const { + return key == KeyTy{getShape(), getElementType(), getLayoutMap()}; + } + + static SequenceTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + const KeyTy &key) { + auto *storage = allocator.allocate(); + return new (storage) SequenceTypeStorage{ + std::get(key), std::get(key), + std::get(key)}; + } + + SequenceType::Shape getShape() const { return shape; } + mlir::Type getElementType() const { return eleTy; } + mlir::AffineMapAttr getLayoutMap() const { return map; } + +protected: + SequenceType::Shape shape; + mlir::Type eleTy; + mlir::AffineMapAttr map; + +private: + SequenceTypeStorage() = delete; + explicit SequenceTypeStorage(const SequenceType::Shape &shape, + mlir::Type eleTy, mlir::AffineMapAttr map) + : shape{shape}, eleTy{eleTy}, map{map} {} +}; + +/// Derived type storage +struct RecordTypeStorage : public mlir::TypeStorage { + using KeyTy = llvm::StringRef; + + static unsigned hashKey(const KeyTy &key) { + return llvm::hash_combine(key.str()); + } + + bool operator==(const KeyTy &key) const { return key == getName(); } + + static RecordTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + const KeyTy &key) { + auto *storage = allocator.allocate(); + return new (storage) RecordTypeStorage{key}; + } + + llvm::StringRef getName() const { return name; } + + void setLenParamList(llvm::ArrayRef list) { + lens = list; + } + llvm::ArrayRef getLenParamList() const { return lens; } + + void setTypeList(llvm::ArrayRef list) { types = list; } + llvm::ArrayRef getTypeList() const { return types; } + + void finalize(llvm::ArrayRef lenParamList, + llvm::ArrayRef typeList) { + if (finalized) + return; + finalized = true; + setLenParamList(lenParamList); + setTypeList(typeList); + } + +protected: + std::string name; + bool finalized; + std::vector lens; + std::vector types; + +private: + RecordTypeStorage() = delete; + explicit RecordTypeStorage(llvm::StringRef name) + : name{name}, finalized{false} {} +}; + +/// Type descriptor type storage +struct TypeDescTypeStorage : public mlir::TypeStorage { + using KeyTy = mlir::Type; + + static unsigned hashKey(const KeyTy &key) { return llvm::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getOfType(); } + + static TypeDescTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + mlir::Type ofTy) { + assert(ofTy && "descriptor type is null"); + auto *storage = allocator.allocate(); + return new (storage) TypeDescTypeStorage{ofTy}; + } + + // The type described by this type descriptor instance + mlir::Type getOfType() const { return ofTy; } + +protected: + mlir::Type ofTy; + +private: + TypeDescTypeStorage() = delete; + explicit TypeDescTypeStorage(mlir::Type ofTy) : ofTy{ofTy} {} +}; + +} // namespace detail + +template +bool inbounds(A v, B lb, B ub) { + return v >= lb && v < ub; +} + +bool isa_fir_type(mlir::Type t) { + return inbounds(t.getKind(), mlir::Type::FIRST_FIR_TYPE, + mlir::Type::LAST_FIR_TYPE); +} + +bool isa_std_type(mlir::Type t) { + return inbounds(t.getKind(), mlir::Type::FIRST_STANDARD_TYPE, + mlir::Type::LAST_STANDARD_TYPE); +} + +bool isa_fir_or_std_type(mlir::Type t) { + return isa_fir_type(t) || isa_std_type(t); +} + +bool isaMemref(mlir::Type t) { + return t.isa() || t.isa() || t.isa(); +} + +bool isanAggregate(mlir::Type t) { + return t.isa() || t.isa(); +} + +mlir::Type dyn_cast_ptrEleTy(mlir::Type t) { + if (auto p = t.dyn_cast()) + return p.getEleTy(); + if (auto p = t.dyn_cast()) + return p.getEleTy(); + if (auto p = t.dyn_cast()) + return p.getEleTy(); + return {}; +} + +} // namespace fir + +// CHARACTER + +CharacterType fir::CharacterType::get(mlir::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_CHARACTER, kind); +} + +int fir::CharacterType::getFKind() const { return getImpl()->getFKind(); } + +// Dims + +DimsType fir::DimsType::get(mlir::MLIRContext *ctxt, unsigned rank) { + return Base::get(ctxt, FIR_DIMS, rank); +} + +int fir::DimsType::getRank() const { return getImpl()->getRank(); } + +// Field + +FieldType fir::FieldType::get(mlir::MLIRContext *ctxt, KindTy) { + return Base::get(ctxt, FIR_FIELD, 0); +} + +// Len + +LenType fir::LenType::get(mlir::MLIRContext *ctxt, KindTy) { + return Base::get(ctxt, FIR_LEN, 0); +} + +// LOGICAL + +LogicalType fir::LogicalType::get(mlir::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_LOGICAL, kind); +} + +int fir::LogicalType::getFKind() const { return getImpl()->getFKind(); } + +// INTEGER + +IntType fir::IntType::get(mlir::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_INT, kind); +} + +int fir::IntType::getFKind() const { return getImpl()->getFKind(); } + +// COMPLEX + +CplxType fir::CplxType::get(mlir::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_COMPLEX, kind); +} + +KindTy fir::CplxType::getFKind() const { return getImpl()->getFKind(); } + +// REAL + +RealType fir::RealType::get(mlir::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_REAL, kind); +} + +int fir::RealType::getFKind() const { return getImpl()->getFKind(); } + +// Box + +BoxType fir::BoxType::get(mlir::Type elementType, mlir::AffineMapAttr map) { + return Base::get(elementType.getContext(), FIR_BOX, elementType, map); +} + +mlir::Type fir::BoxType::getEleTy() const { + return getImpl()->getElementType(); +} + +mlir::AffineMapAttr fir::BoxType::getLayoutMap() const { + return getImpl()->getLayoutMap(); +} + +mlir::LogicalResult fir::BoxType::verifyConstructionInvariants( + const mlir::AttributeStorage *, mlir::Type eleTy, mlir::AffineMapAttr map) { + // TODO + return mlir::success(); +} + +// BoxChar + +BoxCharType fir::BoxCharType::get(mlir::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_BOXCHAR, kind); +} + +CharacterType fir::BoxCharType::getEleTy() const { + return getImpl()->getElementType(getContext()); +} + +// BoxProc + +BoxProcType fir::BoxProcType::get(mlir::Type elementType) { + return Base::get(elementType.getContext(), FIR_BOXPROC, elementType); +} + +mlir::Type fir::BoxProcType::getEleTy() const { + return getImpl()->getElementType(); +} + +mlir::LogicalResult +fir::BoxProcType::verifyConstructionInvariants(const mlir::AttributeStorage *, + mlir::Type eleTy) { + if (eleTy.isa() || eleTy.isa()) + return mlir::success(); + return mlir::failure(); +} + +// Reference + +ReferenceType fir::ReferenceType::get(mlir::Type elementType) { + return Base::get(elementType.getContext(), FIR_REFERENCE, elementType); +} + +mlir::Type fir::ReferenceType::getEleTy() const { + return getImpl()->getElementType(); +} + +mlir::LogicalResult fir::ReferenceType::verifyConstructionInvariants( + const mlir::AttributeStorage *context, mlir::Type eleTy) { + if (eleTy.isa() || eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa()) + return mlir::failure(); + return mlir::success(); +} + +// Pointer + +PointerType fir::PointerType::get(mlir::Type elementType) { + if (!singleIndirectionLevel(elementType)) { + assert(false && "FIXME: invalid element type"); + return {}; + } + return Base::get(elementType.getContext(), FIR_POINTER, elementType); +} + +mlir::Type fir::PointerType::getEleTy() const { + return getImpl()->getElementType(); +} + +mlir::LogicalResult fir::PointerType::verifyConstructionInvariants( + const mlir::AttributeStorage *context, mlir::Type eleTy) { + if (eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa()) + return mlir::failure(); + return mlir::success(); +} + +// Heap + +HeapType fir::HeapType::get(mlir::Type elementType) { + if (!singleIndirectionLevel(elementType)) { + assert(false && "FIXME: invalid element type"); + return {}; + } + return Base::get(elementType.getContext(), FIR_HEAP, elementType); +} + +mlir::Type fir::HeapType::getEleTy() const { + return getImpl()->getElementType(); +} + +mlir::LogicalResult +fir::HeapType::verifyConstructionInvariants(const mlir::AttributeStorage *, + mlir::Type eleTy) { + if (eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa()) + return mlir::failure(); + return mlir::success(); +} + +// Sequence + +SequenceType fir::SequenceType::get(const Shape &shape, mlir::Type elementType, + mlir::AffineMapAttr map) { + auto *ctxt = elementType.getContext(); + return Base::get(ctxt, FIR_SEQUENCE, shape, elementType, map); +} + +mlir::Type fir::SequenceType::getEleTy() const { + return getImpl()->getElementType(); +} + +mlir::AffineMapAttr fir::SequenceType::getLayoutMap() const { + return getImpl()->getLayoutMap(); +} + +SequenceType::Shape fir::SequenceType::getShape() const { + return getImpl()->getShape(); +} + +mlir::LogicalResult fir::SequenceType::verifyConstructionInvariants( + const mlir::AttributeStorage *attributeStorage, + const SequenceType::Shape &shape, mlir::Type eleTy, + mlir::AffineMapAttr map) { + // DIMENSION attribute can only be applied to an intrinsic or record type + if (eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa()) { + auto context = attributeStorage->getDialect().getContext(); + mlir::emitError(mlir::UnknownLoc::get(context), + "cannot build an array of this element type: ") + << eleTy << '\n'; + return mlir::failure(); + } + return mlir::success(); +} + +// compare if two shapes are equivalent +bool fir::operator==(const SequenceType::Shape &sh_1, + const SequenceType::Shape &sh_2) { + if (sh_1.size() != sh_2.size()) + return false; + for (std::size_t i = 0, e{sh_1.size()}; i != e; ++i) + if (sh_1[i] != sh_2[i]) + return false; + return true; +} + +// compute the hash of a Shape +llvm::hash_code fir::hash_value(const SequenceType::Shape &sh) { + if (sh.size()) { + return llvm::hash_combine_range(sh.begin(), sh.end()); + } + return llvm::hash_combine(0); +} + +/// RecordType +/// +/// This type captures a Fortran "derived type" + +RecordType fir::RecordType::get(mlir::MLIRContext *ctxt, llvm::StringRef name) { + return Base::get(ctxt, FIR_DERIVED, name); +} + +void fir::RecordType::finalize(llvm::ArrayRef lenPList, + llvm::ArrayRef typeList) { + getImpl()->finalize(lenPList, typeList); +} + +llvm::StringRef fir::RecordType::getName() { return getImpl()->getName(); } + +RecordType::TypeList fir::RecordType::getTypeList() { + return getImpl()->getTypeList(); +} + +RecordType::TypeList fir::RecordType::getLenParamList() { + return getImpl()->getLenParamList(); +} + +detail::RecordTypeStorage const *fir::RecordType::uniqueKey() const { + return getImpl(); +} + +mlir::LogicalResult +fir::RecordType::verifyConstructionInvariants(const mlir::AttributeStorage *, + llvm::StringRef name) { + if (name.size() == 0) + return mlir::failure(); + return mlir::success(); +} + +mlir::Type fir::RecordType::getType(llvm::StringRef ident) { + for (auto f : getTypeList()) + if (ident == f.first) + return f.second; + assert(false && "query for field not present in record"); + return {}; +} + +/// Type descriptor type +/// +/// This is the type of a type descriptor object (similar to a class instance) + +TypeDescType fir::TypeDescType::get(mlir::Type ofType) { + assert(!ofType.isa()); + return Base::get(ofType.getContext(), FIR_TYPEDESC, ofType); +} + +mlir::Type fir::TypeDescType::getOfTy() const { return getImpl()->getOfType(); } + +mlir::LogicalResult +fir::TypeDescType::verifyConstructionInvariants(const mlir::AttributeStorage *, + mlir::Type eleTy) { + if (eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa() || + eleTy.isa() || eleTy.isa()) + return mlir::failure(); + return mlir::success(); +} + +namespace { + +void printBounds(llvm::raw_ostream &os, const SequenceType::Shape &bounds) { + os << '<'; + for (auto &b : bounds) { + if (b >= 0) { + os << b << 'x'; + } else { + os << "?x"; + } + } +} + +llvm::SmallPtrSet recordTypeVisited; + +} // namespace + +void fir::printFirType(FIROpsDialect *, mlir::Type ty, + mlir::DialectAsmPrinter &p) { + auto &os = p.getStream(); + switch (ty.getKind()) { + case fir::FIR_BOX: { + auto type = ty.cast(); + os << "box<"; + p.printType(type.getEleTy()); + if (auto map = type.getLayoutMap()) { + os << ", "; + p.printAttribute(map); + } + os << '>'; + } break; + case fir::FIR_BOXCHAR: { + auto type = ty.cast().getEleTy(); + os << "boxchar<" << type.cast().getFKind() << '>'; + } break; + case fir::FIR_BOXPROC: + os << "boxproc<"; + p.printType(ty.cast().getEleTy()); + os << '>'; + break; + case fir::FIR_CHARACTER: // intrinsic + os << "char<" << ty.cast().getFKind() << '>'; + break; + case fir::FIR_COMPLEX: // intrinsic + os << "complex<" << ty.cast().getFKind() << '>'; + break; + case fir::FIR_DERIVED: { // derived + auto type = ty.cast(); + os << "type<" << type.getName(); + if (!recordTypeVisited.count(type.uniqueKey())) { + recordTypeVisited.insert(type.uniqueKey()); + if (type.getLenParamList().size()) { + char ch = '('; + for (auto p : type.getLenParamList()) { + os << ch << p.first << ':'; + p.second.print(os); + ch = ','; + } + os << ')'; + } + if (type.getTypeList().size()) { + char ch = '{'; + for (auto p : type.getTypeList()) { + os << ch << p.first << ':'; + p.second.print(os); + ch = ','; + } + os << '}'; + } + recordTypeVisited.erase(type.uniqueKey()); + } + os << '>'; + } break; + case fir::FIR_DIMS: + os << "dims<" << ty.cast().getRank() << '>'; + break; + case fir::FIR_FIELD: + os << "field"; + break; + case fir::FIR_HEAP: + os << "heap<"; + p.printType(ty.cast().getEleTy()); + os << '>'; + break; + case fir::FIR_INT: // intrinsic + os << "int<" << ty.cast().getFKind() << '>'; + break; + case fir::FIR_LEN: + os << "len"; + break; + case fir::FIR_LOGICAL: // intrinsic + os << "logical<" << ty.cast().getFKind() << '>'; + break; + case fir::FIR_POINTER: + os << "ptr<"; + p.printType(ty.cast().getEleTy()); + os << '>'; + break; + case fir::FIR_REAL: // intrinsic + os << "real<" << ty.cast().getFKind() << '>'; + break; + case fir::FIR_REFERENCE: + os << "ref<"; + p.printType(ty.cast().getEleTy()); + os << '>'; + break; + case fir::FIR_SEQUENCE: { + os << "array"; + auto type = ty.cast(); + auto shape = type.getShape(); + if (shape.size()) { + printBounds(os, shape); + } else { + os << "<*:"; + } + p.printType(ty.cast().getEleTy()); + if (auto map = type.getLayoutMap()) { + os << ", "; + map.print(os); + } + os << '>'; + } break; + case fir::FIR_TYPEDESC: + os << "tdesc<"; + p.printType(ty.cast().getOfTy()); + os << '>'; + break; + } +} diff --git a/lib/Optimizer/Support/CMakeLists.txt b/lib/Optimizer/Support/CMakeLists.txt new file mode 100644 index 000000000000..88a1fc78a5f6 --- /dev/null +++ b/lib/Optimizer/Support/CMakeLists.txt @@ -0,0 +1,10 @@ +add_llvm_library(FIRSupport + KindMapping.cpp +) + +target_link_libraries(FIRSupport FIRDialect) + +install (TARGETS FIRSupport + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) diff --git a/lib/Optimizer/Support/KindMapping.cpp b/lib/Optimizer/Support/KindMapping.cpp new file mode 100644 index 000000000000..957486ab0c1b --- /dev/null +++ b/lib/Optimizer/Support/KindMapping.cpp @@ -0,0 +1,239 @@ +//===-- KindMapping.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/Support/KindMapping.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/CommandLine.h" + +/// Allow the user to set the FIR intrinsic type kind value to LLVM type +/// mappings. Note that these are not mappings from kind values to any +/// other MLIR dialect, only to LLVM IR. The default values follow the f18 +/// front-end kind mappings. + +using Bitsize = fir::KindMapping::Bitsize; +using KindTy = fir::KindMapping::KindTy; +using LLVMTypeID = fir::KindMapping::LLVMTypeID; +using MatchResult = fir::KindMapping::MatchResult; + +static llvm::cl::opt ClKindMapping( + "kind-mapping", llvm::cl::desc("kind mapping string to set kind precision"), + llvm::cl::value_desc("kind-mapping-string"), llvm::cl::init("")); + +namespace fir { +namespace { + +/// Integral types default to the kind value being the size of the value in +/// bytes. The default is to scale from bytes to bits. +Bitsize defaultScalingKind(KindTy kind) { + const unsigned BITS_IN_BYTE = 8; + return kind * BITS_IN_BYTE; +} + +/// Floating-point types default to the kind value being the size of the value +/// in bytes. The default is to translate kinds of 2, 4, 8, 10, and 16 to a +/// valid llvm::Type::TypeID value. Otherwise, the default is FloatTyID. +LLVMTypeID defaultRealKind(KindTy kind) { + switch (kind) { + case 2: + return LLVMTypeID::HalfTyID; + case 4: + return LLVMTypeID::FloatTyID; + case 8: + return LLVMTypeID::DoubleTyID; + case 10: + return LLVMTypeID::X86_FP80TyID; + case 16: + return LLVMTypeID::FP128TyID; + default: + return LLVMTypeID::FloatTyID; + } +} + +// lookup the kind-value given the defaults, the mappings, and a KIND key +template +RT doLookup(std::function def, + const std::map> &map, KindTy kind) { + auto iter = map.find(KEY); + if (iter != map.end()) { + auto iter2 = iter->second.find(kind); + if (iter2 != iter->second.end()) + return iter2->second; + } + return def(kind); +} + +// do a lookup for INTERGER, LOGICAL, or CHARACTER +template +Bitsize getIntegerLikeBitsize(KindTy kind, const MAP &map) { + return doLookup(defaultScalingKind, map, kind); +} + +// do a lookup for REAL or COMPLEX +template +LLVMTypeID getFloatLikeTypeID(KindTy kind, const MAP &map) { + return doLookup(defaultRealKind, map, kind); +} + +template +const llvm::fltSemantics &getFloatSemanticsOfKind(KindTy kind, const MAP &map) { + switch (doLookup(defaultRealKind, map, kind)) { + case LLVMTypeID::HalfTyID: + return llvm::APFloat::IEEEhalf(); + case LLVMTypeID::FloatTyID: + return llvm::APFloat::IEEEsingle(); + case LLVMTypeID::DoubleTyID: + return llvm::APFloat::IEEEdouble(); + case LLVMTypeID::X86_FP80TyID: + return llvm::APFloat::x87DoubleExtended(); + case LLVMTypeID::FP128TyID: + return llvm::APFloat::IEEEquad(); + case LLVMTypeID::PPC_FP128TyID: + return llvm::APFloat::PPCDoubleDouble(); + default: + llvm_unreachable("Invalid floating type"); + } +} + +MatchResult parseCode(char &code, const char *&ptr) { + if (*ptr != 'a' && *ptr != 'c' && *ptr != 'i' && *ptr != 'l' && *ptr != 'r') + return {}; + code = *ptr++; + return {true}; +} + +template +MatchResult parseSingleChar(const char *&ptr) { + if (*ptr != ch) + return {}; + ++ptr; + return {true}; +} + +MatchResult parseColon(const char *&ptr) { return parseSingleChar<':'>(ptr); } + +MatchResult parseComma(const char *&ptr) { return parseSingleChar<','>(ptr); } + +MatchResult parseInt(unsigned &result, const char *&ptr) { + const char *beg = ptr; + while (*ptr >= '0' && *ptr <= '9') + ptr++; + if (beg == ptr) + return {}; + llvm::StringRef ref(beg, ptr - beg); + int temp; + if (ref.consumeInteger(10, temp)) + return {}; + result = temp; + return {true}; +} + +bool matchString(const char *&ptr, llvm::StringRef literal) { + llvm::StringRef s(ptr); + if (s.startswith(literal)) { + ptr += literal.size(); + return true; + } + return false; +} + +MatchResult parseTypeID(LLVMTypeID &result, const char *&ptr) { + if (matchString(ptr, "Half")) { + result = LLVMTypeID::HalfTyID; + return {true}; + } + if (matchString(ptr, "Float")) { + result = LLVMTypeID::FloatTyID; + return {true}; + } + if (matchString(ptr, "Double")) { + result = LLVMTypeID::DoubleTyID; + return {true}; + } + if (matchString(ptr, "X86_FP80")) { + result = LLVMTypeID::X86_FP80TyID; + return {true}; + } + if (matchString(ptr, "FP128")) { + result = LLVMTypeID::FP128TyID; + return {true}; + } + return {}; +} + +} // namespace + +KindMapping::KindMapping(mlir::MLIRContext *context, llvm::StringRef map) + : context{context} { + parse(map); +} + +KindMapping::KindMapping(mlir::MLIRContext *context) + : KindMapping{context, ClKindMapping} {} + +MatchResult KindMapping::badMapString(const llvm::Twine &ptr) { + auto unknown = mlir::UnknownLoc::get(context); + mlir::emitError(unknown, ptr); + return {}; +} + +MatchResult KindMapping::parse(llvm::StringRef kindMap) { + if (kindMap.empty()) + return {true}; + const char *srcPtr = kindMap.begin(); + while (true) { + char code = '\0'; + KindTy kind = 0; + if (parseCode(code, srcPtr) || parseInt(kind, srcPtr)) + return badMapString(srcPtr); + if (code == 'a' || code == 'i' || code == 'l') { + Bitsize bits = 0; + if (parseColon(srcPtr) || parseInt(bits, srcPtr)) + return badMapString(srcPtr); + intMap[code][kind] = bits; + } else if (code == 'r' || code == 'c') { + LLVMTypeID id{}; + if (parseColon(srcPtr) || parseTypeID(id, srcPtr)) + return badMapString(srcPtr); + floatMap[code][kind] = id; + } else { + return badMapString(srcPtr); + } + if (parseComma(srcPtr)) + break; + } + if (*srcPtr) + return badMapString(srcPtr); + return {true}; +} + +Bitsize KindMapping::getCharacterBitsize(KindTy kind) { + return getIntegerLikeBitsize<'a'>(kind, intMap); +} + +Bitsize KindMapping::getIntegerBitsize(KindTy kind) { + return getIntegerLikeBitsize<'i'>(kind, intMap); +} + +Bitsize KindMapping::getLogicalBitsize(KindTy kind) { + return getIntegerLikeBitsize<'l'>(kind, intMap); +} + +LLVMTypeID KindMapping::getRealTypeID(KindTy kind) { + return getFloatLikeTypeID<'r'>(kind, floatMap); +} + +LLVMTypeID KindMapping::getComplexTypeID(KindTy kind) { + return getFloatLikeTypeID<'c'>(kind, floatMap); +} + +const llvm::fltSemantics &KindMapping::getFloatSemantics(KindTy kind) { + return getFloatSemanticsOfKind<'r'>(kind, floatMap); +} + +} // namespace fir diff --git a/test-lit/CMakeLists.txt b/test-lit/CMakeLists.txt index 111814303b0b..72a2c17cb547 100644 --- a/test-lit/CMakeLists.txt +++ b/test-lit/CMakeLists.txt @@ -21,6 +21,11 @@ set(FLANG_TEST_DEPENDS count not ) + +if (LINK_WITH_FIR) + list(APPEND FLANG_TEST_DEPENDS tco) +endif() + add_lit_testsuite(check-all "Running the Flang regression tests" ${CMAKE_CURRENT_BINARY_DIR} PARAMS ${FLANG_TEST_PARAMS} diff --git a/test-lit/Fir/fir-ops.fir b/test-lit/Fir/fir-ops.fir new file mode 100644 index 000000000000..661cf4c5835c --- /dev/null +++ b/test-lit/Fir/fir-ops.fir @@ -0,0 +1,458 @@ +// Test the FIR operations + +// RUN: tco -emit-fir %s | tco -emit-fir | FileCheck %s +// UNSUPPORTED: !fir + +// Fortran Intrinsic types +// CHECK-LABEL: func @it1() -> !fir.int<4> +// CHECK-LABEL: func @it2() -> !fir.real<8> +// CHECK-LABEL: func @it3() -> !fir.complex<8> +// CHECK-LABEL: func @it4() -> !fir.logical<1> +// CHECK-LABEL: func @it5() -> !fir.char<1> +func @it1() -> !fir.int<4> +func @it2() -> !fir.real<8> +func @it3() -> !fir.complex<8> +func @it4() -> !fir.logical<1> +func @it5() -> !fir.char<1> + +// Fortran Derived types (records) +// CHECK-LABEL: func @dvd1() -> !fir.type +// CHECK-LABEL: func @dvd2() -> !fir.type +// CHECK-LABEL: func @dvd3() -> !fir.type +// CHECK-LABEL: func @dvd4() -> !fir.type +func @dvd1() -> !fir.type +func @dvd2() -> !fir.type +func @dvd3() -> !fir.type +func @dvd4() -> !fir.type + +// FIR array types +// CHECK-LABEL: func @arr1() -> !fir.array<10xf32> +// CHECK-LABEL: func @arr2() -> !fir.array<10x10xf32> +// CHECK-LABEL: func @arr3() -> !fir.array +// CHECK-LABEL: func @arr4() -> !fir.array<10x?xf32> +// CHECK-LABEL: func @arr5() -> !fir.array +// CHECK-LABEL: func @arr6() -> !fir.array<*:f32> +func @arr1() -> !fir.array<10xf32> +func @arr2() -> !fir.array<10x10xf32> +func @arr3() -> !fir.array +func @arr4() -> !fir.array<10x?xf32> +func @arr5() -> !fir.array +func @arr6() -> !fir.array<*:f32> + +// FIR pointer-like types +// CHECK-LABEL: func @mem1() -> !fir.ref +// CHECK-LABEL: func @mem2() -> !fir.ptr +// CHECK-LABEL: func @mem3() -> !fir.heap +// CHECK-LABEL: func @mem4() -> !fir.ref<() -> ()> +func @mem1() -> !fir.ref +func @mem2() -> !fir.ptr +func @mem3() -> !fir.heap +func @mem4() -> !fir.ref<() -> ()> + +// FIR box types (descriptors) +// CHECK-LABEL: func @box1() -> !fir.box> +// CHECK-LABEL: func @box2() -> !fir.boxchar<2> +// CHECK-LABEL: func @box3() -> !fir.boxproc<(i32, i32) -> i64> +// CHECK-LABEL: func @box4() -> !fir.box> +func @box1() -> !fir.box> +func @box2() -> !fir.boxchar<2> +func @box3() -> !fir.boxproc<(i32, i32) -> i64> +func @box4() -> !fir.box> + +// FIR misc. types +// CHECK-LABEL: func @oth1() -> !fir.dims<1> +// CHECK-LABEL: func @oth2() -> !fir.field +// CHECK-LABEL: func @oth3() -> !fir.tdesc> +func @oth1() -> !fir.dims<1> +func @oth2() -> !fir.field +func @oth3() -> !fir.tdesc> + +// Fortran SUBROUTINE and FUNCTION +// CHECK-LABEL: func @print_index3(index, index, index) +// CHECK-LABEL: func @user_i64(i64) +// CHECK-LABEL: func @user_tdesc(!fir.tdesc>) +func @print_index3(index, index, index) +func @user_i64(i64) +func @user_tdesc(!fir.tdesc>) + +// expect the void return to be omitted +// CHECK-LABEL: func @store_tuple(tuple>) +func @store_tuple(tuple>) -> () + +// CHECK-LABEL: func @get_method_box() -> !fir.box> +// CHECK-LABEL: func @method_impl(!fir.box>) +func @get_method_box() -> !fir.box> +func @method_impl(!fir.box>) + +// CHECK-LABEL: func @nop() +func @nop() + +// CHECK-LABEL: func @get_func() -> (() -> ()) +func @get_func() -> (() -> ()) + +// CHECK-LABEL: @instructions +func @instructions() { + // CHECK: %[[A0:.*]] = fir.alloca !fir.array<10xi32> + %0 = fir.alloca !fir.array<10xi32> + // CHECK: fir.load %[[A0]] : !fir.ref> + %1 = fir.load %0 : !fir.ref> + %2 = fir.alloca i32 + %3 = constant 22 : i32 + // CHECK: fir.store %{{.*}} to %{{.*}} : !fir.ref + fir.store %3 to %2 : !fir.ref + // CHECK: fir.undefined i32 + %4 = fir.undefined i32 + // CHECK: %[[A5:.*]] = fir.allocmem !fir.array<100xf32> + %5 = fir.allocmem !fir.array<100xf32> + // CHECK: %[[A6:.*]] = fir.embox %[[A5]] : (!fir.heap>) -> !fir.box> + %6 = fir.embox %5 : (!fir.heap>) -> !fir.box> + // CHECK: fir.box_addr %{{.*}} : (!fir.box>) -> !fir.ref> + %7 = fir.box_addr %6 : (!fir.box>) -> !fir.ref> + %c0 = constant 0 : index + // CHECK: %[[A8:.*]]:3 = fir.box_dims %{{.*}}, %{{.*}} : (!fir.box>, index) -> (index, index, index) + %d1:3 = fir.box_dims %6, %c0 : (!fir.box>, index) -> (index, index, index) + // CHECK: fir.call @print_index3(%[[A8]]#0, %[[A8]]#1, %[[A8]]#2) : (index, index, index) + fir.call @print_index3(%d1#0, %d1#1, %d1#2) : (index, index, index) -> () + %8 = fir.call @it1() : () -> !fir.int<4> + // CHECK: fir.box_elesize %[[A6]] : (!fir.box>) -> i64 + %9 = fir.box_elesize %6 : (!fir.box>) -> i64 + // CHECK: fir.box_isalloc %[[A6]] : (!fir.box>) -> i1 + %10 = fir.box_isalloc %6 : (!fir.box>) -> i1 + // CHECK: fir.box_isarray %[[A6]] : (!fir.box>) -> i1 + %11 = fir.box_isarray %6 : (!fir.box>) -> i1 + // CHECK: fir.box_isptr %[[A6]] : (!fir.box>) -> i1 + %12 = fir.box_isptr %6 : (!fir.box>) -> i1 + // CHECK: fir.box_rank %[[A6]] : (!fir.box>) -> i64 + %13 = fir.box_rank %6 : (!fir.box>) -> i64 + // CHECK: fir.box_tdesc %[[A6]] : (!fir.box>) -> !fir.tdesc> + %14 = fir.box_tdesc %6 : (!fir.box>) -> !fir.tdesc> + %15 = fir.call @box2() : () -> !fir.boxchar<2> + // CHECK: fir.boxchar_len %{{.*}} : (!fir.boxchar<2>) -> i32 + %16 = fir.boxchar_len %15 : (!fir.boxchar<2>) -> i32 + %17 = fir.call @box3() : () -> !fir.boxproc<(i32, i32) -> i64> + // CHECK: fir.boxproc_host %{{.*}} : (!fir.boxproc<(i32, i32) -> i64>) -> !fir.ref + %18 = fir.boxproc_host %17 : (!fir.boxproc<(i32, i32) -> i64>) -> !fir.ref + %19 = constant 10 : i32 + // CHECK: fir.coordinate_of %{{.*}}, %{{.*}} : (!fir.heap>, i32) -> !fir.ref + %20 = fir.coordinate_of %5, %19 : (!fir.heap>, i32) -> !fir.ref + // CHECK: fir.field_index f, !fir.type + %21 = fir.field_index f, !fir.type + // CHECK: fir.undefined !fir.type + %22 = fir.undefined !fir.type + // CHECK: fir.extract_value %{{.*}}, %{{.*}} : (!fir.type, !fir.field) -> f32 + %23 = fir.extract_value %22, %21 : (!fir.type, !fir.field) -> f32 + %c1 = constant 1 : i32 + // CHECK: fir.gendims %{{.*}}, %{{.*}}, %{{.*}} : (i32, i32, i32) -> !fir.dims<1> + %24 = fir.gendims %c1, %19, %c1 : (i32, i32, i32) -> !fir.dims<1> + %cf1 = constant 1.0 : f32 + // CHECK: fir.insert_value %{{.*}}, %{{.*}}, %{{.*}} : (!fir.type, f32, !fir.field) -> !fir.type + %25 = fir.insert_value %22, %cf1, %21 : (!fir.type, f32, !fir.field) -> !fir.type + // CHECK: fir.len_param_index f, !fir.type + %26 = fir.len_param_index f, !fir.type + %27 = fir.call @box4() : () -> !fir.box> + // CHECK: fir.dispatch "method"(%{{.*}}) : (!fir.box>) -> i32 + %28 = fir.dispatch "method"(%27) : (!fir.box>) -> i32 + // CHECK: fir.convert %{{.*}} : (i32) -> i64 + %29 = fir.convert %28 : (i32) -> i64 + // CHECK: fir.gentypedesc !fir.type + %30 = fir.gentypedesc !fir.type + fir.call @user_tdesc(%30) : (!fir.tdesc>) -> () + // CHECK: fir.no_reassoc %{{.*}} : i64 + %31 = fir.no_reassoc %29 : i64 + fir.call @user_i64(%31) : (i64) -> () + // CHECK: fir.freemem %{{.*}} : !fir.heap> + fir.freemem %5 : !fir.heap> + %32 = fir.call @get_func() : () -> (() -> ()) + fir.call %32() : () -> () + // CHECK: fir.address_of (@it1) : !fir.ref<() -> !fir.int<4>> + %33 = fir.address_of (@it1) : !fir.ref<() -> !fir.int<4>> + return +} + +// CHECK-LABEL: @boxing_match +func @boxing_match() { + %0 = fir.alloca i32 + %d6 = fir.alloca !fir.type + %d3 = fir.alloca !fir.char<1> + %e6 = fir.alloca tuple + %1 = fir.embox %0 : (!fir.ref) -> !fir.box + // CHECK: fir.unbox %{{.*}} : (!fir.box) -> (!fir.ref, i32, i32, !fir.tdesc, i32, !fir.dims<0>) + %2:6 = fir.unbox %1 : (!fir.box) -> (!fir.ref,i32,i32,!fir.tdesc,i32,!fir.dims<0>) + %c8 = constant 8 : i32 + %3 = fir.undefined !fir.char<1> + // CHECK: fir.emboxchar %{{.*}}, %{{.*}} : (!fir.ref>, i32) -> !fir.boxchar<1> + // CHECK: fir.unboxchar %{{.*}} : (!fir.boxchar<1>) -> (!fir.ref>, i32) + %4 = fir.emboxchar %d3, %c8 : (!fir.ref>, i32) -> !fir.boxchar<1> + %5:2 = fir.unboxchar %4 : (!fir.boxchar<1>) -> (!fir.ref>, i32) + %6 = fir.undefined !fir.type + %z = constant 0 : i32 + %c12 = constant 12 : i32 + %a2 = fir.insert_value %6, %c12, %z : (!fir.type, i32, i32) -> !fir.type + %z1 = constant 1 : i32 + %c42 = constant 42.13 : f64 + %a3 = fir.insert_value %6, %c42, %z1 : (!fir.type, f64, i32) -> !fir.type + fir.store %a3 to %d6 : !fir.ref> + %7 = fir.emboxproc @method_impl, %e6 : ((!fir.box>) -> (), !fir.ref>) -> !fir.boxproc<(!fir.box>) -> ()> + %8:2 = fir.unboxproc %7 : (!fir.boxproc<(!fir.box>) -> ()>) -> ((!fir.box>) -> (), !fir.ref>>) + // CHECK: fir.emboxproc @method_impl, %{{.*}} : ((!fir.box>) -> (), !fir.ref>) -> !fir.boxproc<(!fir.box>) -> ()> + // CHECK: fir.unboxproc %{{.*}} : (!fir.boxproc<(!fir.box>) -> ()>) -> ((!fir.box>) -> (), !fir.ref>>) + %9 = call @box3() : () -> !fir.boxproc<(i32, i32) -> i64> + %10:2 = fir.unboxproc %9 : (!fir.boxproc<(i32, i32) -> i64>) -> ((i32, i32) -> i64, !fir.ref>>) + %11 = fir.load %10#1 : !fir.ref>> + fir.call @store_tuple(%11) : (tuple>) -> () + return +} + +// CHECK-LABEL: @loop +func @loop() { + %c1 = constant 1 : index + %c10 = constant 10 : index + %ct = constant true + // CHECK: fir.loop %{{.*}} = %{{.*}} to %{{.*}} { + %i = fir.loop %i = %c1 to %c10 { + // CHECK: fir.where %{{.*}} { + fir.where %ct { + fir.call @nop() : () -> () + // CHECK: } otherwise { + } otherwise { + fir.call @nop() : () -> () + } + } + // CHECK: fir.unreachable + fir.unreachable +} + +// CHECK-LABEL: @bar_select +func @bar_select(%arg : i32, %arg2 : i32) -> i32 { + %0 = constant 1 : i32 + %1 = constant 2 : i32 + %2 = constant 3 : i32 + %3 = constant 4 : i32 + // CHECK: fir.select %{{.*}} : i32 [1, ^bb1(%{{.*}} : i32), 2, ^bb2(%{{.*}}, %{{.*}}, %{{.*}} : i32, i32, i32), -3, ^bb3(%{{.*}}, %{{.*}} : i32, i32), 4, ^bb4(%{{.*}} : i32), unit, ^bb5] + fir.select %arg:i32 [ 1,^bb1(%0:i32), 2,^bb2(%2,%arg,%arg2:i32,i32,i32), -3,^bb3(%arg2,%2:i32,i32), 4,^bb4(%1:i32), unit,^bb5 ] +^bb1(%a : i32) : + return %a : i32 +^bb2(%b : i32, %b2 : i32, %b3:i32) : + %4 = addi %b, %b2 : i32 + %5 = addi %4, %b3 : i32 + return %5 : i32 +^bb3(%c:i32, %c2:i32) : + %6 = addi %c, %c2 : i32 + return %6 : i32 +^bb4(%d : i32) : + return %d : i32 +^bb5 : + %zero = constant 0 : i32 + return %zero : i32 +} + +// CHECK-LABEL: @bar_select_rank +func @bar_select_rank(%arg : i32, %arg2 : i32) -> i32 { + %0 = constant 1 : i32 + %1 = constant 2 : i32 + %2 = constant 3 : i32 + %3 = constant 4 : i32 + // CHECK: fir.select_rank %{{.*}} : i32 [1, ^bb1(%{{.*}} : i32), 2, ^bb2(%{{.*}}, %{{.*}}, %{{.*}} : i32, i32, i32), 3, ^bb3(%{{.*}}, %{{.*}} : i32, i32), -1, ^bb4(%{{.*}} : i32), unit, ^bb5] + fir.select_rank %arg:i32 [ 1,^bb1(%0:i32), 2,^bb2(%2,%arg,%arg2:i32,i32,i32), 3,^bb3(%arg2,%2:i32,i32), -1,^bb4(%1:i32), unit,^bb5 ] +^bb1(%a : i32) : + return %a : i32 +^bb2(%b : i32, %b2 : i32, %b3:i32) : + %4 = addi %b, %b2 : i32 + %5 = addi %4, %b3 : i32 + return %5 : i32 +^bb3(%c:i32, %c2:i32) : + %6 = addi %c, %c2 : i32 + return %6 : i32 +^bb4(%d : i32) : + return %d : i32 +^bb5 : + %zero = constant 0 : i32 + %7 = fir.call @get_method_box() : () -> !fir.box> + fir.dispatch method(%7) : (!fir.box>) -> () + return %zero : i32 +} + +// CHECK-LABEL: @bar_select_type +func @bar_select_type(%arg : !fir.box}>>) -> i32 { + %0 = constant 1 : i32 + %1 = constant 2 : i32 + %2 = constant 3 : i32 + %3 = constant 4 : i32 + // CHECK: fir.select_type %{{.*}} : !fir.box}>> [#fir.instance>, ^bb1(%{{.*}} : i32), #fir.instance>, ^bb2(%{{.*}} : i32), #fir.subsumed>, ^bb3(%{{.*}} : i32), #fir.instance>, ^bb4(%{{.*}} : i32), unit, ^bb5] + fir.select_type %arg : !fir.box}>> [ #fir.instance>,^bb1(%0:i32), #fir.instance>,^bb2(%2:i32), #fir.subsumed>,^bb3(%2:i32), #fir.instance>,^bb4(%1:i32), unit,^bb5 ] +^bb1(%a : i32) : + return %a : i32 +^bb2(%b : i32) : + return %b : i32 +^bb3(%c : i32) : + return %c : i32 +^bb4(%d : i32) : + return %d : i32 +^bb5 : + %zero = constant 0 : i32 + return %zero : i32 +} + +// CHECK-LABEL: @bar_select_case +func @bar_select_case(%arg : i32, %arg2 : i32) -> i32 { + %0 = constant 1 : i32 + %1 = constant 2 : i32 + %2 = constant 3 : i32 + %3 = constant 4 : i32 + // CHECK: fir.select_case %{{.*}} : i32 [#fir.point, %{{.*}}, ^bb1(%{{.*}} : i32), #fir.lower, %{{.*}}, ^bb2(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}} : i32, i32, i32, i32), #fir.interval, %{{.*}}, %{{.*}}, ^bb3(%{{.*}}, %{{.*}} : i32, i32), #fir.upper, %{{.*}}, ^bb4(%{{.*}} : i32), unit, ^bb5] + fir.select_case %arg : i32 [#fir.point, %0, ^bb1(%0:i32), #fir.lower, %1, ^bb2(%2,%arg,%arg2,%1:i32,i32,i32,i32), #fir.interval, %2, %3, ^bb3(%2,%arg2:i32,i32), #fir.upper, %arg, ^bb4(%1:i32), unit, ^bb5] +^bb1(%a : i32) : + return %a : i32 +^bb2(%b : i32, %b2:i32, %b3:i32, %b4:i32) : + %4 = addi %b, %b2 : i32 + %5 = muli %4, %b3 : i32 + %6 = addi %5, %b4 : i32 + return %6 : i32 +^bb3(%c : i32, %c2 : i32) : + %7 = addi %c, %c2 : i32 + return %7 : i32 +^bb4(%d : i32) : + return %d : i32 +^bb5 : + %zero = constant 0 : i32 + return %zero : i32 +} + +// CHECK-LABEL: @global_var +fir.global @global_var : i32 { + %0 = constant 1 : i32 + fir.has_value %0 : i32 +} + +// CHECK-LABEL: @global_constant +fir.global @global_constant constant : i32 { + %0 = constant 934 : i32 + fir.has_value %0 : i32 +} + +// CHECK-LABEL: @global_derived +fir.global @global_derived : !fir.type { + // CHECK: fir.global_len "f", 1 : i32 + fir.global_len f, 1 : i32 + %0 = fir.undefined !fir.type + fir.has_value %0 : !fir.type +} + +// CHECK-LABEL: @dispatch_tbl +fir.dispatch_table @dispatch_tbl { + // CHECK: fir.dt_entry "method", @method_impl + fir.dt_entry "method", @method_impl +} + +// CHECK-LABEL: @compare_real +func @compare_real(%a : !fir.real<16>, %b : !fir.real<16>) { + // CHECK: fir.cmpf "false", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "oeq", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "ogt", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "oge", %{{.*}}, %{{.*}} : !fir.real<16> + %d0 = fir.cmpf "false", %a, %b : !fir.real<16> + %d1 = fir.cmpf "oeq", %a, %b : !fir.real<16> + %d2 = fir.cmpf "ogt", %a, %b : !fir.real<16> + %d3 = fir.cmpf "oge", %a, %b : !fir.real<16> + // CHECK: fir.cmpf "olt", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "ole", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "one", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "ord", %{{.*}}, %{{.*}} : !fir.real<16> + %a0 = fir.cmpf "olt", %a, %b : !fir.real<16> + %a1 = fir.cmpf "ole", %a, %b : !fir.real<16> + %a2 = fir.cmpf "one", %a, %b : !fir.real<16> + %a3 = fir.cmpf "ord", %a, %b : !fir.real<16> + // CHECK: fir.cmpf "ueq", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "ugt", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "uge", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "ult", %{{.*}}, %{{.*}} : !fir.real<16> + %b0 = fir.cmpf "ueq", %a, %b : !fir.real<16> + %b1 = fir.cmpf "ugt", %a, %b : !fir.real<16> + %b2 = fir.cmpf "uge", %a, %b : !fir.real<16> + %b3 = fir.cmpf "ult", %a, %b : !fir.real<16> + // CHECK: fir.cmpf "ule", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "une", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "uno", %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.cmpf "true", %{{.*}}, %{{.*}} : !fir.real<16> + %c0 = fir.cmpf "ule", %a, %b : !fir.real<16> + %c1 = fir.cmpf "une", %a, %b : !fir.real<16> + %c2 = fir.cmpf "uno", %a, %b : !fir.real<16> + %c3 = fir.cmpf "true", %a, %b : !fir.real<16> + return +} + +// CHECK-LABEL: @compare_complex +func @compare_complex(%a : !fir.complex<16>, %b : !fir.complex<16>) { + // CHECK: fir.cmpc "false", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "oeq", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "ogt", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "oge", %{{.*}}, %{{.*}} : !fir.complex<16> + %d0 = fir.cmpc "false", %a, %b : !fir.complex<16> + %d1 = fir.cmpc "oeq", %a, %b : !fir.complex<16> + %d2 = fir.cmpc "ogt", %a, %b : !fir.complex<16> + %d3 = fir.cmpc "oge", %a, %b : !fir.complex<16> + // CHECK: fir.cmpc "olt", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "ole", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "one", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "ord", %{{.*}}, %{{.*}} : !fir.complex<16> + %a0 = fir.cmpc "olt", %a, %b : !fir.complex<16> + %a1 = fir.cmpc "ole", %a, %b : !fir.complex<16> + %a2 = fir.cmpc "one", %a, %b : !fir.complex<16> + %a3 = fir.cmpc "ord", %a, %b : !fir.complex<16> + // CHECK: fir.cmpc "ueq", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "ugt", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "uge", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "ult", %{{.*}}, %{{.*}} : !fir.complex<16> + %b0 = fir.cmpc "ueq", %a, %b : !fir.complex<16> + %b1 = fir.cmpc "ugt", %a, %b : !fir.complex<16> + %b2 = fir.cmpc "uge", %a, %b : !fir.complex<16> + %b3 = fir.cmpc "ult", %a, %b : !fir.complex<16> + // CHECK: fir.cmpc "ule", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "une", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "uno", %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.cmpc "true", %{{.*}}, %{{.*}} : !fir.complex<16> + %c0 = fir.cmpc "ule", %a, %b : !fir.complex<16> + %c1 = fir.cmpc "une", %a, %b : !fir.complex<16> + %c2 = fir.cmpc "uno", %a, %b : !fir.complex<16> + %c3 = fir.cmpc "true", %a, %b : !fir.complex<16> + return +} + +// CHECK-LABEL: @arith_real +func @arith_real(%a : !fir.real<16>, %b : !fir.real<16>) -> !fir.real<16> { + %c1 = constant 1.0 : f32 + %0 = fir.convert %c1 : (f32) -> !fir.real<16> + // CHECK: %[[R1:.*]] = fir.negf %{{.*}} : !fir.real<16> + // CHECK: fir.addf %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: %[[R3:.*]] = fir.subf %{{.*}}, %{{.*}} : !fir.real<16> + // CHECK: fir.mulf %[[R1]], %[[R3]] : !fir.real<16> + // CHECK: fir.divf %{{.*}}, %{{.*}} : !fir.real<16> + %1 = fir.negf %a : !fir.real<16> + %2 = fir.addf %0, %1 : !fir.real<16> + %3 = fir.subf %2, %b : !fir.real<16> + %4 = fir.mulf %1, %3 : !fir.real<16> + %5 = fir.divf %4, %a : !fir.real<16> + return %5 : !fir.real<16> +} + +// CHECK-LABEL: @arith_complex +func @arith_complex(%a : !fir.complex<16>, %b : !fir.complex<16>) -> !fir.complex<16> { + // CHECK: fir.negc %{{.*}} : !fir.complex<16> + // CHECK: fir.addc %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.subc %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.mulc %{{.*}}, %{{.*}} : !fir.complex<16> + // CHECK: fir.divc %{{.*}}, %{{.*}} : !fir.complex<16> + %1 = fir.negc %a : !fir.complex<16> + %2 = fir.addc %b, %1 : !fir.complex<16> + %3 = fir.subc %2, %b : !fir.complex<16> + %4 = fir.mulc %1, %3 : !fir.complex<16> + %5 = fir.divc %4, %a : !fir.complex<16> + return %5 : !fir.complex<16> +} + +// CHECK-LABEL: @character_literal +func @character_literal() -> !fir.array<13 x !fir.char<1>> { + // CHECK: fir.string_lit "Hello, World!"(13) : !fir.char<1> + %0 = fir.string_lit "Hello, World!"(13) : !fir.char<1> + return %0 : !fir.array<13 x !fir.char<1>> +} diff --git a/test-lit/Fir/fir-types.fir b/test-lit/Fir/fir-types.fir new file mode 100644 index 000000000000..a9e194c1f15b --- /dev/null +++ b/test-lit/Fir/fir-types.fir @@ -0,0 +1,70 @@ +// Test the FIR types + +// RUN: tco -emit-fir %s | tco -emit-fir | FileCheck %s +// UNSUPPORTED: !fir + +// CHECK-LABEL: func @it1() -> !fir.int<4> +// CHECK-LABEL: func @it2() -> !fir.real<8> +// CHECK-LABEL: func @it3() -> !fir.complex<8> +// CHECK-LABEL: func @it4() -> !fir.logical<1> +// CHECK-LABEL: func @it5() -> !fir.char<1> +func @it1() -> !fir.int<4> +func @it2() -> !fir.real<8> +func @it3() -> !fir.complex<8> +func @it4() -> !fir.logical<1> +func @it5() -> !fir.char<1> + +// CHECK-LABEL: func @dvd1() -> !fir.type +// CHECK-LABEL: func @dvd2() -> !fir.type +// CHECK-LABEL: func @dvd3() -> !fir.type +// CHECK-LABEL: func @dvd4() -> !fir.type +// CHECK-LABEL: func @dvd5() -> !fir.type +// CHECK-LABEL: func @dvd6() -> !fir.type>}> +func @dvd1() -> !fir.type +func @dvd2() -> !fir.type +func @dvd3() -> !fir.type +func @dvd4() -> !fir.type +func @dvd5() -> !fir.type +func @dvd6() -> !fir.type>}> + +// CHECK-LABEL: func @arr1() -> !fir.array<10xf32> +// CHECK-LABEL: func @arr2() -> !fir.array<10x10xf32> +// CHECK-LABEL: func @arr3() -> !fir.array +// CHECK-LABEL: func @arr4() -> !fir.array<10x?xf32> +// CHECK-LABEL: func @arr5() -> !fir.array +// CHECK-LABEL: func @arr6() -> !fir.array<*:f32> +// CHECK-LABEL: func @arr7() -> !fir.array<1x2x?x4x5x6x7x8x9xf32> +func @arr1() -> !fir.array<10xf32> +func @arr2() -> !fir.array<10x10xf32> +func @arr3() -> !fir.array +func @arr4() -> !fir.array<10x?xf32> +func @arr5() -> !fir.array +func @arr6() -> !fir.array<*:f32> +func @arr7() -> !fir.array<1x2x?x4x5x6x7x8x9xf32> + +// CHECK-LABEL: func @mem1() -> !fir.ref +// CHECK-LABEL: func @mem2() -> !fir.ptr +// CHECK-LABEL: func @mem3() -> !fir.heap +// CHECK-LABEL: func @mem4() -> !fir.ref<() -> ()> +func @mem1() -> !fir.ref +func @mem2() -> !fir.ptr +func @mem3() -> !fir.heap +func @mem4() -> !fir.ref<() -> ()> + +// CHECK-LABEL: func @box1() -> !fir.box> +// CHECK-LABEL: func @box2() -> !fir.boxchar<2> +// CHECK-LABEL: func @box3() -> !fir.boxproc<(i32, i32) -> i64> +// CHECK-LABEL: func @box4() -> !fir.box +func @box1() -> !fir.box> +func @box2() -> !fir.boxchar<2> +func @box3() -> !fir.boxproc<(i32, i32) -> i64> +func @box4() -> !fir.box + +// CHECK-LABEL: func @oth1() -> !fir.dims<1> +// CHECK-LABEL: func @oth2() -> !fir.field +// CHECK-LABEL: func @oth3() -> !fir.tdesc> +// CHECK-LABEL: func @oth4() -> !fir.dims<15> +func @oth1() -> !fir.dims<1> +func @oth2() -> !fir.field +func @oth3() -> !fir.tdesc> +func @oth4() -> !fir.dims<15> diff --git a/test-lit/lit.cfg.py b/test-lit/lit.cfg.py index 3ca3c8ad23f7..2dce888c33fd 100644 --- a/test-lit/lit.cfg.py +++ b/test-lit/lit.cfg.py @@ -28,7 +28,7 @@ # suffixes: A list of file extensions to treat as test files. config.suffixes = ['.f', '.F', '.ff','.FOR', '.for', '.f77', '.f90', '.F90', '.ff90', '.f95', '.F95', '.ff95', '.fpp', '.FPP', '.cuf', - '.CUF', '.f18', '.F18'] + '.CUF', '.f18', '.F18', '.fir' ] # test_source_root: The root path where tests are located. config.test_source_root = os.path.dirname(__file__) @@ -56,6 +56,12 @@ llvm_config.with_environment('PATH', config.flang_tools_dir, append_path=True) llvm_config.with_environment('PATH', config.llvm_tools_dir, append_path=True) +# For builds with FIR, set path for tco and enable related tests +if config.flang_llvm_tools_dir != "" : + config.available_features.add('fir') + if config.llvm_tools_dir != config.flang_llvm_tools_dir : + llvm_config.with_environment('PATH', config.flang_llvm_tools_dir, append_path=True) + # For each occurrence of a flang tool name, replace it with the full path to # the build directory holding that tool. We explicitly specify the directories # to search to ensure that we get the tools just built and not some random diff --git a/test-lit/lit.site.cfg.py.in b/test-lit/lit.site.cfg.py.in index d00f3856fb41..fe428f9bee06 100644 --- a/test-lit/lit.site.cfg.py.in +++ b/test-lit/lit.site.cfg.py.in @@ -7,6 +7,7 @@ config.flang_obj_root = "@FLANG_BINARY_DIR@" config.flang_src_dir = "@FLANG_SOURCE_DIR@" config.flang_tools_dir = "@FLANG_TOOLS_DIR@" config.flang_intrinsic_modules_dir = "@FLANG_INTRINSIC_MODULES_DIR@" +config.flang_llvm_tools_dir = "@LLVM_RUNTIME_OUTPUT_INTDIR@" config.python_executable = "@PYTHON_EXECUTABLE@" # Support substitution of the tools_dir with user parameters. This is diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 41983a7b2e39..a02679436d14 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -6,5 +6,7 @@ # #===------------------------------------------------------------------------===# - add_subdirectory(f18) +if(LINK_WITH_FIR) + add_subdirectory(tco) +endif() diff --git a/tools/tco/CMakeLists.txt b/tools/tco/CMakeLists.txt new file mode 100644 index 000000000000..f08134e16b9e --- /dev/null +++ b/tools/tco/CMakeLists.txt @@ -0,0 +1,22 @@ +set(LIBS + FIRDialect + FIRSupport + MLIRAllDialects + MLIRIR + MLIRLLVMIR + MLIRPass + MLIRStandardToLLVM + MLIRTransforms + MLIRAffineToStandard + MLIRAnalysis + MLIRLoopToStandard + MLIREDSC + MLIRParser + MLIRStandardToLLVM + MLIRSupport + MLIRVectorToLLVM +) + +add_llvm_tool(tco tco.cpp) +llvm_update_compile_flags(tco) +target_link_libraries(tco PRIVATE ${LIBS}) diff --git a/tools/tco/tco.cpp b/tools/tco/tco.cpp new file mode 100644 index 000000000000..c7e613a1d725 --- /dev/null +++ b/tools/tco/tco.cpp @@ -0,0 +1,118 @@ +//===- tco.cpp - Tilikum Crossing Opt ---------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This is to be like LLVM's opt program, only for FIR. Such a program is +// required for roundtrip testing, etc. +// +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Dialect/FIRDialect.h" +#include "flang/Optimizer/Support/KindMapping.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Module.h" +#include "mlir/InitAllDialects.h" +#include "mlir/Parser.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Transforms/Passes.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +using namespace llvm; + +cl::opt inputFilename(cl::Positional, cl::desc(""), + cl::init("-")); + +cl::opt outputFilename("o", cl::desc("Specify output filename"), + cl::value_desc("filename"), cl::init("-")); + +cl::opt emitFir("emit-fir", cl::desc("Parse and pretty-print the input"), + cl::init(false)); + +void printModuleBody(mlir::ModuleOp mod, raw_ostream &output) { + // don't output the terminator bogo-op + auto e{--mod.end()}; + for (auto i{mod.begin()}; i != e; ++i) { + i->print(output); + output << "\n\n"; + } +} + +// compile a .fir file +int compileFIR() { + // check that there is a file to load + ErrorOr> fileOrErr = + MemoryBuffer::getFileOrSTDIN(inputFilename); + + if (std::error_code EC = fileOrErr.getError()) { + errs() << "Could not open file: " << EC.message() << '\n'; + return 1; + } + + // load the file into a module + SourceMgr sourceMgr; + sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), SMLoc()); + auto context = std::make_unique(); + auto owningRef = mlir::parseSourceFile(sourceMgr, context.get()); + + if (!owningRef) { + errs() << "Error can't load file " << inputFilename << '\n'; + return 2; + } + if (mlir::failed(owningRef->verify())) { + errs() << "Error verifying FIR module\n"; + return 4; + } + + std::error_code ec; + ToolOutputFile out(outputFilename, ec, sys::fs::OF_None); + + // run passes + mlir::PassManager pm{context.get()}; + mlir::applyPassManagerCLOptions(pm); + if (emitFir) { + // parse the input and pretty-print it back out + // -emit-fir intentionally disables all the passes + } else { + // add all the passes + // the user can disable them individually + } + + // run the pass manager + if (mlir::succeeded(pm.run(*owningRef))) { + // passes ran successfully, so keep the output + if (emitFir) + printModuleBody(*owningRef, out.os()); + out.keep(); + return 0; + } + + // pass manager failed + printModuleBody(*owningRef, errs()); + errs() << "\nFAILED: " << inputFilename << '\n'; + return 8; +} +} // namespace + +int main(int argc, char **argv) { + mlir::registerAllDialects(); + fir::registerFIR(); + fir::registerFIRPasses(); + [[maybe_unused]] InitLLVM y(argc, argv); + mlir::registerPassManagerCLOptions(); + mlir::PassPipelineCLParser passPipe("", "Compiler passes to run"); + cl::ParseCommandLineOptions(argc, argv, "Tilikum Crossing Optimizer\n"); + return compileFIR(); +}