From aee59682c6b934eed64df13c3bfd59620af7a083 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Mon, 16 Dec 2019 09:39:58 -0800 Subject: [PATCH 001/123] add back removed files --- .../fir/Analysis/IteratedDominanceFrontier.h | 97 + include/fir/Attribute.h | 123 ++ include/fir/CMakeLists.txt | 4 + include/fir/Dialect.h | 54 + include/fir/FIROps.h | 107 + include/fir/FIROps.td | 1768 +++++++++++++++++ include/fir/Transforms/MemToReg.h | 39 + include/fir/Type.h | 277 +++ lib/CMakeLists.txt | 1 + lib/burnside/CMakeLists.txt | 25 + lib/burnside/builder.h | 78 + lib/burnside/flattened.cc | 695 +++++++ lib/burnside/flattened.h | 237 +++ lib/burnside/mixin.h | 72 + 14 files changed, 3577 insertions(+) create mode 100644 include/fir/Analysis/IteratedDominanceFrontier.h create mode 100644 include/fir/Attribute.h create mode 100644 include/fir/CMakeLists.txt create mode 100644 include/fir/Dialect.h create mode 100644 include/fir/FIROps.h create mode 100644 include/fir/FIROps.td create mode 100644 include/fir/Transforms/MemToReg.h create mode 100644 include/fir/Type.h create mode 100644 lib/burnside/CMakeLists.txt create mode 100644 lib/burnside/builder.h create mode 100644 lib/burnside/flattened.cc create mode 100644 lib/burnside/flattened.h create mode 100644 lib/burnside/mixin.h diff --git a/include/fir/Analysis/IteratedDominanceFrontier.h b/include/fir/Analysis/IteratedDominanceFrontier.h new file mode 100644 index 000000000000..c8858d0a8f70 --- /dev/null +++ b/include/fir/Analysis/IteratedDominanceFrontier.h @@ -0,0 +1,97 @@ +//===- IteratedDominanceFrontier.h - Calculate IDF --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// Compute iterated dominance frontiers using a linear time algorithm. +/// +/// The algorithm used here is based on: +/// +/// Sreedhar and Gao. A linear time algorithm for placing phi-nodes. +/// In Proceedings of the 22nd ACM SIGPLAN-SIGACT Symposium on Principles of +/// Programming Languages +/// POPL '95. ACM, New York, NY, 62-73. +/// +/// It has been modified to not explicitly use the DJ graph data structure and +/// to directly compute pruned SSA using per-variable liveness information. +// +//===----------------------------------------------------------------------===// + +#ifndef FIR_ANALYSIS_IDF_H +#define FIR_ANALYSIS_IDF_H + +#include "mlir/Analysis/Dominance.h" +#include "mlir/IR/Block.h" +#include "mlir/Support/LLVM.h" + +namespace mlir { +class Block; +class DominanceInfo; +} // namespace mlir + +namespace fir { + +/// Determine the iterated dominance frontier, given a set of defining +/// blocks, and optionally, a set of live-in blocks. +/// +/// In turn, the results can be used to place phi nodes. +/// +/// This algorithm is a linear time computation of Iterated Dominance Frontiers, +/// pruned using the live-in set. +/// By default, liveness is not used to prune the IDF computation. +/// The template parameters should be either BasicBlock* or Inverse, depending on if you want the forward or reverse IDF. +template +class IDFCalculator { +public: + IDFCalculator(mlir::DominanceInfo &DT) : DT(DT), useLiveIn(false) {} + + /// Give the IDF calculator the set of blocks in which the value is + /// defined. This is equivalent to the set of starting blocks it should be + /// calculating the IDF for (though later gets pruned based on liveness). + /// + /// Note: This set *must* live for the entire lifetime of the IDF calculator. + void setDefiningBlocks(const llvm::SmallPtrSetImpl &Blocks) { + DefBlocks = &Blocks; + } + + /// Give the IDF calculator the set of blocks in which the value is + /// live on entry to the block. This is used to prune the IDF calculation to + /// not include blocks where any phi insertion would be dead. + /// + /// Note: This set *must* live for the entire lifetime of the IDF calculator. + + void setLiveInBlocks(const llvm::SmallPtrSetImpl &Blocks) { + LiveInBlocks = &Blocks; + useLiveIn = true; + } + + /// Reset the live-in block set to be empty, and tell the IDF + /// calculator to not use liveness anymore. + void resetLiveInBlocks() { + LiveInBlocks = nullptr; + useLiveIn = false; + } + + /// Calculate iterated dominance frontiers + /// + /// This uses the linear-time phi algorithm based on DJ-graphs mentioned in + /// the file-level comment. It performs DF->IDF pruning using the live-in + /// set, to avoid computing the IDF for blocks where an inserted PHI node + /// would be dead. + void calculate(llvm::SmallVectorImpl &IDFBlocks); + +private: + mlir::DominanceInfo &DT; + bool useLiveIn; + const llvm::SmallPtrSetImpl *LiveInBlocks; + const llvm::SmallPtrSetImpl *DefBlocks; +}; + +typedef IDFCalculator ForwardIDFCalculator; + +} // namespace fir +#endif diff --git a/include/fir/Attribute.h b/include/fir/Attribute.h new file mode 100644 index 000000000000..c71a8862c4d6 --- /dev/null +++ b/include/fir/Attribute.h @@ -0,0 +1,123 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIR_ATTRIBUTE_H +#define FIR_ATTRIBUTE_H + +#include "mlir/IR/Attributes.h" + +namespace fir { + +class FIROpsDialect; + +namespace detail { +struct TypeAttributeStorage; +} + +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, +}; + +class ExactTypeAttr + : public mlir::Attribute::AttrBase { +public: + using Base::Base; + using ValueType = mlir::Type; + + static llvm::StringRef getAttrName() { return "instance"; } + static ExactTypeAttr get(mlir::Type value); + + mlir::Type getType() const; + + constexpr static bool kindof(unsigned kind) { return kind == getId(); } + constexpr static unsigned getId() { return AttributeKind::FIR_EXACTTYPE; } +}; + +class SubclassAttr + : public mlir::Attribute::AttrBase { +public: + using Base::Base; + using ValueType = mlir::Type; + + static llvm::StringRef getAttrName() { return "subsumed"; } + static SubclassAttr get(mlir::Type value); + + mlir::Type getType() const; + + constexpr static bool kindof(unsigned kind) { return kind == getId(); } + constexpr static unsigned getId() { return AttributeKind::FIR_SUBCLASS; } +}; + +class ClosedIntervalAttr + : public mlir::Attribute::AttrBase { +public: + using Base::Base; + + static llvm::StringRef getAttrName() { return "interval"; } + static ClosedIntervalAttr get(mlir::MLIRContext *ctxt); + constexpr static bool kindof(unsigned kind) { return kind == getId(); } + constexpr static unsigned getId() { + return AttributeKind::FIR_CLOSEDCLOSED_INTERVAL; + } +}; + +class UpperBoundAttr : public mlir::Attribute::AttrBase { +public: + using Base::Base; + + static llvm::StringRef getAttrName() { return "upper"; } + static UpperBoundAttr get(mlir::MLIRContext *ctxt); + constexpr static bool kindof(unsigned kind) { return kind == getId(); } + constexpr static unsigned getId() { + return AttributeKind::FIR_OPENCLOSED_INTERVAL; + } +}; + +class LowerBoundAttr : public mlir::Attribute::AttrBase { +public: + using Base::Base; + + static llvm::StringRef getAttrName() { return "lower"; } + static LowerBoundAttr get(mlir::MLIRContext *ctxt); + constexpr static bool kindof(unsigned kind) { return kind == getId(); } + constexpr static unsigned getId() { + return AttributeKind::FIR_CLOSEDOPEN_INTERVAL; + } +}; + +class PointIntervalAttr : public mlir::Attribute::AttrBase { +public: + using Base::Base; + + static llvm::StringRef getAttrName() { return "point"; } + static PointIntervalAttr get(mlir::MLIRContext *ctxt); + constexpr static bool kindof(unsigned kind) { return kind == getId(); } + constexpr static unsigned getId() { return AttributeKind::FIR_POINT; } +}; + +mlir::Attribute parseFirAttribute(FIROpsDialect *dialect, + llvm::StringRef rawText, mlir::Type type, + mlir::Location loc); + +} // namespace fir + +#endif // FIR_ATTRIBUTE_H diff --git a/include/fir/CMakeLists.txt b/include/fir/CMakeLists.txt new file mode 100644 index 000000000000..9528b1abffb0 --- /dev/null +++ b/include/fir/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/fir/Dialect.h b/include/fir/Dialect.h new file mode 100644 index 000000000000..d4a180cecce9 --- /dev/null +++ b/include/fir/Dialect.h @@ -0,0 +1,54 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIR_DIALECT_H +#define FIR_DIALECT_H + +#include "mlir/IR/Dialect.h" + +namespace llvm { +class raw_ostream; +class StringRef; +} // namespace llvm + +namespace mlir { +class Attribute; +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(llvm::StringRef rawData, + mlir::Location loc) const override; + void printType(mlir::Type ty, llvm::raw_ostream &os) const override; + + mlir::Attribute parseAttribute(llvm::StringRef rawText, mlir::Type type, + mlir::Location loc) const override; + void printAttribute(mlir::Attribute attr, + llvm::raw_ostream &os) const override; +}; + +} // namespace fir + +#endif // FIR_DIALECT_H diff --git a/include/fir/FIROps.h b/include/fir/FIROps.h new file mode 100644 index 000000000000..3a9bbf99af04 --- /dev/null +++ b/include/fir/FIROps.h @@ -0,0 +1,107 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIR_FIROPS_H +#define FIR_FIROPS_H + +#include "mlir/IR/Builders.h" +#include "mlir/IR/OpImplementation.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +using namespace mlir; +using llvm::ArrayRef; +using llvm::StringRef; + +namespace fir { + +class FirEndOp; + +/// `fir.global` is a typed symbol with an optional list of initializers. +class GlobalOp + : public mlir::Op< + GlobalOp, mlir::OpTrait::ZeroOperands, mlir::OpTrait::ZeroResult, + mlir::OpTrait::IsIsolatedFromAbove, + mlir::OpTrait::SingleBlockImplicitTerminator::Impl> { +public: + using Op::Op; + using Op::print; + + static llvm::StringRef getOperationName() { return "fir.global"; } + static llvm::StringRef getTypeAttrName() { return "type"; } + + static void build(mlir::Builder *builder, mlir::OperationState *result, + llvm::StringRef name, mlir::Type type, + llvm::ArrayRef attrs); + + /// Operation hooks. + static mlir::ParseResult parse(mlir::OpAsmParser &parser, + mlir::OperationState &result); + void print(mlir::OpAsmPrinter &p); + mlir::LogicalResult verify(); + + mlir::Type getType() { + return getAttrOfType(getTypeAttrName()).getValue(); + } + + void appendInitialValue(mlir::Operation *op); + +private: + mlir::Region &front(); +}; + +/// `fir.dispatch_table` is an untyped symbol that is a list of associations +/// between method identifiers and a FuncOp symbol. +class DispatchTableOp + : public mlir::Op< + DispatchTableOp, mlir::OpTrait::ZeroOperands, + mlir::OpTrait::ZeroResult, mlir::OpTrait::IsIsolatedFromAbove, + mlir::OpTrait::SingleBlockImplicitTerminator::Impl> { +public: + using Op::Op; + + static llvm::StringRef getOperationName() { return "fir.dispatch_table"; } + + static void build(mlir::Builder *builder, mlir::OperationState *result, + llvm::StringRef name, mlir::Type type, + llvm::ArrayRef attrs); + + /// Operation hooks. + static mlir::ParseResult parse(mlir::OpAsmParser &parser, + mlir::OperationState &result); + void print(mlir::OpAsmPrinter &p); + mlir::LogicalResult verify(); + + void appendTableEntry(mlir::Operation *op); + +private: + mlir::Region &front(); +}; + +mlir::ParseResult isValidCaseAttr(mlir::Attribute attr); +unsigned getCaseArgumentOffset(llvm::ArrayRef cases, + unsigned dest); +mlir::ParseResult parseSelector(mlir::OpAsmParser *parser, + mlir::OperationState *result, + mlir::OpAsmParser::OperandType &selector, + mlir::Type &type); + +#define GET_OP_CLASSES +#include "fir/FIROps.h.inc" + +LoopOp getForInductionVarOwner(mlir::Value *val); + +} // namespace fir + +#endif // FIR_FIROPS_H diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td new file mode 100644 index 000000000000..7e4b24afb703 --- /dev/null +++ b/include/fir/FIROps.td @@ -0,0 +1,1768 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +// Definition of the FIR dialect operations +// + +#ifdef FIR_DIALECT_FIR_OPS +#else +#define FIR_DIALECT_FIR_OPS + +#ifdef OP_BASE +#else +include "mlir/IR/OpBase.td" +#endif + +def fir_Dialect : Dialect { + let name = "fir"; +} + +def fir_Type : Type, + "FIR dialect type">; + +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">; + +def AnyIntegerLike : TypeConstraint, "any integer">; +def AnyLogicalLike : TypeConstraint, "any logical">; +def AnyRealLike : TypeConstraint, "any real">; +def AnyIntegerType : Type; + +def fir_RecordType : Type()">, + "FIR derived type">; +def fir_SequenceType : Type()">, + "array type">; + +def AnyCompositeLike : TypeConstraint, "any composite">; + +def fir_ReferenceType : Type()">, + "reference type">; +def fir_HeapType : Type()">, + "allocatable type">; +def fir_PointerType : Type()">, + "pointer type">; + +def AnyReferenceLike : TypeConstraint, "any reference">; + +def fir_BoxType : Type()">, "box type">; +def fir_BoxCharType : Type()">, + "box character type">; +def fir_BoxProcType : Type()">, + "box procedure type">; + +def AnyBoxLike : TypeConstraint, "any box">; + +def AnyRefOrBox : TypeConstraint, + "any reference or box">; + +def fir_DimsType : Type()">, "dim type">; +def fir_TypeDescType : Type()">, + "type desc type">; +def fir_FieldType : Type()">, "field type">; + +def AnyCoordinateLike : TypeConstraint, + "any coordinate index">; +def AnyCoordinateType : Type; + +class fir_Op traits> + : Op; + +def SimplePrettyParserPrinter { +} + +class fir_SimpleOp traits> + : fir_Op { + let parser = [{ + M::FunctionType type; + M::OpAsmParser::OperandType opnd; + if (parser.parseOperand(opnd) || + parser.parseColonType(type) || + parser.resolveOperand(opnd, type.getInput(0), result.operands) || + parser.addTypesToList(type.getResults(), result.types)) + return M::failure(); + return M::success(); + }]; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(*getODSOperands(0).begin()); + p << " : "; + p.printFunctionalType(getOperation()); + }]; +} + +def fir_AllocateOpBuilder : OpBuilder< + "Builder *, OperationState &result, Type resultType," + "ArrayRef size = {}, ArrayRef attributes = {}", + [{ + result.addTypes(resultType); + result.addOperands(size); + for (auto namedAttr : attributes) + result.addAttribute(namedAttr.first, namedAttr.second); + }]>; + +def fir_NamedAllocateOpBuilder : OpBuilder< + "Builder *builder, OperationState &result, Type resultType," + "StringRef name, ArrayRef size = {}," + "ArrayRef attributes = {}", + [{ + result.addAttribute("name", builder->getStringAttr(name)); + result.addTypes(resultType); + result.addOperands(size); + 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); + } + }]>; + +class fir_OneResultOp traits = []> : + fir_Op, Results<(outs fir_Type:$res)> { + let builders = [fir_OneResultOpBuilder]; +} + +class fir_SimpleOneResultOp traits = []> : + fir_SimpleOp, Results<(outs fir_Type:$res)> { + 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 Variadic:$shape)> { +} + +// 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 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. + }]; + + let parser = [{ + M::Type intype; + if (parser.parseType(intype)) + return M::failure(); + auto &builder = parser.getBuilder(); + result.addAttribute("in_type", builder.getTypeAttr(intype)); + if (!parser.parseOptionalComma()) { + llvm::SmallVector typeVec; + llvm::SmallVector operands; + if (parser.parseOperandList(operands, + M::OpAsmParser::Delimiter::None) || + parser.resolveOperands(operands, builder.getIndexType(), + parser.getNameLoc(), result.operands)) + return M::failure(); + } + M::Type restype; + if (parser.parseColonType(restype) || + parser.addTypeToList(restype, result.types)) + return M::failure(); + return M::success(); + }]; + let printer = [{ + p << getOperationName() << ' ' << getAttr("in_type"); + for (auto sh : getShapeOperands()) { + p << ", "; + p.printOperand(sh); + } + p << " : " << getType(); + p.printOptionalAttrDict(getAttrs(), {"in_type"}); + }]; + let verifier = [{ + M::Type inType = getAttrOfType("in_type").getValue(); + M::Type outType = getType(); + if (auto ref = outType.dyn_cast()) { + if (inType != ref.getEleTy()) + return emitOpError("input type and !fir.ref element type mismatch"); + } else { + return emitOpError("must be a !fir.ref type"); + } + return M::success(); + }]; + + let extraClassDeclaration = [{ + mlir::Type getAllocatedType(); + operand_range getShapeOperands() { + return {operand_begin(), operand_end()}; + } + }]; +} + +def fir_LoadOp : fir_OneResultOp<"load", [NoSideEffect]>, + Arguments<(ins AnyReferenceLike:$memref)> { + let summary = "load a value from a memory reference"; + + let description = [{ + Load a value from a memory reference into a virtual register. Produces + an immutable ssa-value of the referent type. + }]; + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, Value *refVal", + [{ + fir::ReferenceType refTy = + refVal->getType().cast(); + result.addOperands(refVal); + result.addTypes(refTy.getEleTy()); + }] + >]; + + let parser = [{ + M::Type type; + M::OpAsmParser::OperandType oper; + if (parser.parseOperand(oper) || + parser.parseColonType(type) || + parser.resolveOperand(oper, type, result.operands)) + return M::failure(); + M::Type eleTy; + if (getElementOf(eleTy, type) || + parser.addTypeToList(eleTy, result.types)) + return M::failure(); + return M::success(); + }]; + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(memref()); + p << " : " << memref()->getType(); + p.printOptionalAttrDict(getAttrs(), {}); + }]; + + let extraClassDeclaration = [{ + static mlir::ParseResult getElementOf(mlir::Type &ele, mlir::Type ref); + }]; +} + +def fir_StoreOp : fir_Op<"store", []>, + Arguments<(ins AnyType:$value, AnyReferenceLike:$memref)> { + 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. + }]; + + let parser = [{ + M::Type type; + M::OpAsmParser::OperandType oper; + M::OpAsmParser::OperandType store; + if (parser.parseOperand(oper) || + parser.parseKeyword("to") || + parser.parseOperand(store) || + parser.parseColonType(type) || + parser.resolveOperand(oper, elementType(type), + result.operands) || + parser.resolveOperand(store, type, result.operands)) + return M::failure(); + return M::success(); + }]; + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(value()); + p << " to "; + p.printOperand(memref()); + p << " : " << memref()->getType(); + p.printOptionalAttrDict(getAttrs(), {}); + }]; + + 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. + }]; + + let parser = [{ + M::Type intype; + if (parser.parseType(intype) || + parser.addTypeToList(intype, result.types)) + return M::failure(); + auto &builder = parser.getBuilder(); + result.addAttribute("in_type", builder.getTypeAttr(intype)); + return M::success(); + }]; + let printer = [{ + p << getOperationName() << ' ' << getAttr("in_type"); + }]; + let verifier = [{ + if (auto ref = getType().dyn_cast()) + return emitOpError("undefined values of type !fir.ref not allowed"); + return M::success(); + }]; +} + +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. + }]; + + let parser = [{ + M::Type intype; + if (parser.parseType(intype)) + return M::failure(); + auto &builder = parser.getBuilder(); + result.addAttribute("in_type", builder.getTypeAttr(intype)); + if (!parser.parseOptionalComma()) { + llvm::SmallVector typeVec; + llvm::SmallVector operands; + if (parser.parseOperandList(operands, + M::OpAsmParser::Delimiter::None) || + parser.resolveOperands(operands, builder.getIndexType(), + parser.getNameLoc(), result.operands)) + return M::failure(); + } + M::Type restype; + if (parser.parseColonType(restype) || + parser.addTypeToList(restype, result.types)) + return M::failure(); + return M::success(); + }]; + let printer = [{ + p << getOperationName() << ' ' << getAttr("in_type"); + for (auto sh : getShapeOperands()) { + p << ", "; + p.printOperand(sh); + } + p << " : " << getType(); + p.printOptionalAttrDict(getAttrs(), {"in_type"}); + }]; + let verifier = [{ + M::Type inType = getAttrOfType("in_type").getValue(); + M::Type outType = getType(); + if (auto ref = outType.dyn_cast()) { + if (inType != ref.getEleTy()) + return emitOpError("input type and !fir.heap element type mismatch"); + } else { + return emitOpError("must be a !fir.heap type"); + } + return M::success(); + }]; + + let extraClassDeclaration = [{ + mlir::Type getAllocatedType(); + operand_range getShapeOperands() { + return {operand_begin(), operand_end()}; + } + }]; +} + +def fir_FreeMemOp : fir_Op<"freemem", []>, + Arguments<(ins fir_HeapType:$heapref)> { + 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. + }]; + + let parser = [{ + M::Type type; + M::OpAsmParser::OperandType oper; + if (parser.parseOperand(oper) || + parser.parseColonType(type) || + parser.resolveOperand(oper, type, result.operands)) + return M::failure(); + return M::success(); + }]; + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(heapref()); + p << " : " << getOperand()->getType(); + p.printOptionalAttrDict(getAttrs(), {}); + }]; +} + +// Terminator operations + +class fir_SwitchTerminatorOp traits = []> : + fir_Op, + Arguments<(ins Variadic:$args)>, 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 *; + constexpr static 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 = [{ + M::OpAsmParser::OperandType selector; + M::Type type; + if (parseSelector(parser, result, selector, type)) + return M::failure(); + + llvm::SmallVector ivalues; + llvm::SmallVector dests; + llvm::SmallVector, 4> destArgs; + while (true) { + M::Attribute ivalue; + M::Block *dest; + llvm::SmallVector destArg; + llvm::SmallVector temp; + if (parser.parseAttribute(ivalue, "i", temp) || + parser.parseComma() || + parser.parseSuccessorAndUseList(dest, destArg)) + return M::failure(); + ivalues.push_back(ivalue); + dests.push_back(dest); + destArgs.push_back(destArg); + if (!parser.parseOptionalRSquare()) + break; + if (parser.parseComma()) + return M::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 M::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 M::success(); + }]; + + 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. + }]; +} + +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. + }]; +} + +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. + }]; + + let parser = [{ + M::OpAsmParser::OperandType selector; + M::Type type; + if (parseSelector(parser, result, selector, type)) + return M::failure(); + + llvm::SmallVector attrs; + llvm::SmallVector opers; + llvm::SmallVector dests; + llvm::SmallVector, 4> destArgs; + while (true) { + M::Attribute attr; + M::Block *dest; + llvm::SmallVector destArg; + llvm::SmallVector temp; + if (parser.parseAttribute(attr, "a", temp) || + isValidCaseAttr(attr) || + parser.parseComma()) + return M::failure(); + attrs.push_back(attr); + if (attr.dyn_cast_or_null()) { + } else if (attr.dyn_cast_or_null()) { + M::OpAsmParser::OperandType oper1; + M::OpAsmParser::OperandType oper2; + if (parser.parseOperand(oper1) || + parser.parseComma() || + parser.parseOperand(oper2) || + parser.parseComma()) + return M::failure(); + opers.push_back(oper1); + opers.push_back(oper2); + } else { + M::OpAsmParser::OperandType oper; + if (parser.parseOperand(oper) || + parser.parseComma()) + return M::failure(); + opers.push_back(oper); + } + if (parser.parseSuccessorAndUseList(dest, destArg)) + return M::failure(); + dests.push_back(dest); + destArgs.push_back(destArg); + if (!parser.parseOptionalRSquare()) + break; + if (parser.parseComma()) + return M::failure(); + } + result.addAttribute(AttrName, parser.getBuilder().getArrayAttr(attrs)); + if (parser.resolveOperands(opers, type, result.operands)) + return M::failure(); + for (unsigned i = 0, count = dests.size(); i != count; ++i) + result.addSuccessor(dests[i], destArgs[i]); + return M::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"); + for (unsigned i = 0, count = getNumConditions(); i != count; ++i) { + } + return M::success(); + }]; + + 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. + }]; + + let parser = [{ + M::OpAsmParser::OperandType selector; + M::Type type; + if (parseSelector(parser, result, selector, type)) + return M::failure(); + + llvm::SmallVector attrs; + llvm::SmallVector dests; + llvm::SmallVector, 4> destArgs; + while (true) { + M::Attribute attr; + M::Block *dest; + llvm::SmallVector destArg; + llvm::SmallVector temp; + if (parser.parseAttribute(attr, "a", temp) || + parser.parseComma() || + parser.parseSuccessorAndUseList(dest, destArg)) + return M::failure(); + attrs.push_back(attr); + dests.push_back(dest); + destArgs.push_back(destArg); + if (!parser.parseOptionalRSquare()) + break; + if (parser.parseComma()) + return M::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 M::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()) { + // ok + } else if (attr.dyn_cast_or_null()) { + // ok + } else if (attr.dyn_cast_or_null()) { + // ok + } else { + return emitOpError("invalid type-case alternative"); + } + } + return M::success(); + }]; + 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. + }]; + + let parser = [{ + return M::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 (and only) terminator + for these operations. It is implicit and need not appear in the textual + representation. + }]; +} + +// Operations on !fir.box type objects + +def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]>, Results<(outs fir_BoxType)>, + Arguments<(ins AnyReferenceLike:$memref, Variadic:$dims)> { + 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. + }]; + + let parser = [{ + mlir::FunctionType type; + llvm::SmallVector operands; + mlir::OpAsmParser::OperandType memref; + if (parser.parseOperand(memref)) + return mlir::failure(); + operands.push_back(memref); + if (!parser.parseOptionalComma()) { + mlir::OpAsmParser::OperandType dims; + if (parser.parseOperand(dims)) + return mlir::failure(); + operands.push_back(dims); + } + if (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 (getNumOperands() == 2) { + p << ", "; + p.printOperands(dims()); + } + p << " : "; + p.printFunctionalType(getOperation()); + }]; +} + +def fir_EmboxCharOp : fir_Op<"emboxchar", [NoSideEffect]>, + Results<(outs fir_BoxCharType)>, + Arguments<(ins fir_CharacterType:$memref, AnyIntegerLike:$len)> { + 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. + }]; + + let parser = [{ + mlir::FunctionType type; + llvm::SmallVector operands; + if (parser.parseOperandList(operands, 2, + mlir::OpAsmParser::Delimiter::None) || + 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()); + p << ", "; + p.printOperand(len()); + p << " : "; + p.printFunctionalType(getOperation()); + }]; +} + +def fir_EmboxProcOp : fir_Op<"emboxproc", [NoSideEffect]>, + Results<(outs fir_BoxProcType)>, + Arguments<(ins SymbolRefAttr:$funcname, AnyReferenceLike:$host)> { + 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. An internal + procedure may require a host instance for execution. + }]; + + let parser = [{ + M::FunctionType type; + M::SymbolRefAttr procref; + M::OpAsmParser::OperandType tupleref; + if (parser.parseAttribute(procref, "funcname", result.attributes)) + return M::failure(); + bool hasTuple = false; + if (!parser.parseOptionalComma()) { + if (parser.parseOperand(tupleref)) + return M::failure(); + hasTuple = true; + } + if (parser.parseColonType(type)) + return M::failure(); + if (hasTuple) { + auto input = type.getInputs()[1]; + if (parser.resolveOperand(tupleref, input, result.operands)) + return M::failure(); + } + if (parser.addTypesToList(type.getResults(), result.types)) + return M::failure(); + return M::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr("funcname"); + auto *h = host(); + if (h) { + p << ", "; + p.printOperand(h); + } + p << " : "; + p.printFunctionalType(getOperation()); + }]; +} + +def fir_UnboxOp : fir_SimpleOp<"unbox", [NoSideEffect]>, + Results<(outs fir_ReferenceType, AnyInteger, AnyInteger, fir_TypeDescType, + AnyInteger, fir_DimsType)>, + Arguments<(ins fir_BoxType:$box)> { + let summary = "unbox the boxed value into a tuple value"; + + let description = [{ + Unboxes a value of `box` type into a tuple of information abstracted in + that boxed value. + }]; +} + +def fir_UnboxCharOp : fir_SimpleOp<"unboxchar", [NoSideEffect]>, + Results<(outs fir_ReferenceType, AnyInteger)>, + Arguments<(ins fir_BoxCharType:$boxchar)> { + 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. + }]; +} + +def fir_UnboxProcOp : fir_SimpleOp<"unboxproc", [NoSideEffect]>, + Results<(outs FunctionType, fir_ReferenceType)>, + Arguments<(ins fir_BoxProcType:$boxproc)> { + 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. + }]; +} + +def fir_BoxAddrOp : fir_SimpleOneResultOp<"box_addr", [NoSideEffect]>, + Arguments<(ins fir_BoxType:$val)> { + 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. + }]; + + let parser = [{ + mlir::FunctionType type; + mlir::OpAsmParser::OperandType opnd; + if (parser.parseOperand(opnd) || + parser.parseColonType(type) || + parser.resolveOperand(opnd, type.getInput(0), result.operands) || + parser.addTypesToList(type.getResults(), result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(val()); + p << " : "; + p.printFunctionalType(getOperation()); + }]; +} + +def fir_BoxCharLenOp : fir_SimpleOp<"boxchar_len", [NoSideEffect]>, + Results<(outs AnyInteger)>, Arguments<(ins fir_BoxCharType:$val)> { + let summary = "return the LEN type parameter from a boxchar value"; + + let description = [{ + Extracts the LEN type parameter from a `boxchar` value. + }]; +} + +def fir_BoxDimsOp : fir_Op<"box_dims", [NoSideEffect]>, + Results<(outs AnyIntegerLike, AnyIntegerLike, AnyIntegerLike)>, + Arguments<(ins fir_BoxType:$val, AnyIntegerLike:$dim)> { + 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. + }]; + + let parser = [{ + mlir::FunctionType type; + llvm::SmallVector operands; + if (parser.parseOperandList(operands, 2, + mlir::OpAsmParser::Delimiter::None) || + 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(val()); + p << ", "; + p.printOperand(dim()); + p << " : "; + p.printFunctionalType(getOperation()); + }]; +} + +def fir_BoxEleSizeOp : fir_SimpleOneResultOp<"box_elesize", [NoSideEffect]>, + Arguments<(ins fir_BoxType:$val)> { + 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. + }]; +} + +def fir_BoxIsAllocOp : fir_SimpleOp<"box_isalloc", [NoSideEffect]>, + Results<(outs BoolLike)>, Arguments<(ins fir_BoxType:$val)> { + let summary = "is the boxed value an ALLOCATABLE?"; + + let description = [{ + Determine if the boxed value was from an ALLOCATABLE entity. + + %ref = ... : !fir.heap + %box = fir.embox %ref : !fir.box + %isheap = fir.box_isalloc %box : i1 + }]; +} + +def fir_BoxIsArrayOp : fir_SimpleOp<"box_isarray", [NoSideEffect]>, + Results<(outs BoolLike)>, Arguments<(ins fir_BoxType:$val)> { + let summary = "is the boxed value an array?"; + + let description = [{ + Determine if the boxed value has a positive (> 0) rank. + + %ref = ... : !fir.ref + %dims = fir.gendims(1, 100, 1) : !fir.dims<1> + %box = fir.embox %ref, %dims : !fir.box + %isarr = fir.box_isarray %box : i1 + }]; +} + +def fir_BoxIsPtrOp : fir_SimpleOp<"box_isptr", [NoSideEffect]>, + Results<(outs BoolLike)>, Arguments<(ins fir_BoxType:$val)> { + let summary = "is the boxed value a POINTER?"; + + let description = [{ + Determine if the boxed value was from a POINTER entity. + + %ptr = ... : !fir.ptr + %box = fir.embox %ptr : !fir.box + %isptr = fir.box_isptr %box : i1 + }]; +} + +def fir_BoxProcHostOp : fir_SimpleOp<"boxproc_host", [NoSideEffect]>, + Results<(outs fir_ReferenceType)>, Arguments<(ins fir_BoxProcType:$val)> { + let summary = "returns the host instance pointer (or null)"; + + let description = [{ + Extract the host context pointer from a `boxproc` value. + }]; +} + +def fir_BoxRankOp : fir_SimpleOneResultOp<"box_rank", [NoSideEffect]>, + Arguments<(ins fir_BoxType:$val)> { + 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. + }]; +} + +def fir_BoxTypeDescOp : fir_SimpleOneResultOp<"box_tdesc", [NoSideEffect]>, + Arguments<(ins fir_BoxType:$val)> { + let summary = "return the type descriptor for the boxed value"; + + let description = [{ + Return the opaque type descriptor of a value of `box` type. + }]; +} + +// Record and array type operations + +def fir_CoordinateOp : fir_Op<"coordinate_of", [NoSideEffect]>, + Results<(outs fir_ReferenceType)>, Arguments<(ins AnyRefOrBox:$ref, + Variadic:$coor)> { + let summary = "Finds the coordinate (location) of a value in memory"; + + let description = [{ + Determine a memory reference given a memory reference of composite type + and a list of index values. + }]; + + let parser = [{ + mlir::FunctionType type; + llvm::SmallVector operands; + if (parser.parseOperandList(operands, + mlir::OpAsmParser::Delimiter::None) || + 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(ref()); + p << ", "; + p.printOperands(coor()); + p << " : "; + p.printFunctionalType(getOperation()); + }]; +} + +def fir_ExtractValueOp : fir_OneResultOp<"extract_value", [NoSideEffect]>, + Arguments<(ins AnyCompositeLike:$adt, Variadic:$coor)> { + let summary = "Extract a value from an aggregate SSA-value"; + + let description = [{ + Extract a subobject value given a value of composite type and a list of + index values. + }]; + + let parser = [{ + mlir::FunctionType type; + llvm::SmallVector operands; + if (parser.parseOperandList(operands, + mlir::OpAsmParser::Delimiter::None) || + 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(adt()); + p << ", "; + p.printOperands(coor()); + p << " : "; + p.printFunctionalType(getOperation()); + }]; +} + +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. + }]; + + let arguments = (ins StrAttr:$field_id); + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, StringRef fieldName", + [{ + result.addAttribute("name", builder->getStringAttr(fieldName)); + }] + >]; + + let parser = [{ + mlir::StringAttr attr; + mlir::Type type; + if (parser.parseLParen() || + parser.parseAttribute(attr, "field_id", result.attributes) || + parser.parseRParen() || + parser.parseColonType(type) || + parser.addTypeToList(type, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << " (" << getAttr("field_id") << ") : " + << getType(); + }]; +} + +def fir_GenDimsOp : fir_OneResultOp<"gendims", [NoSideEffect]>, + Arguments<(ins Variadic:$triples)> { + 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 non-negative and the + stride must not be zero. + }]; + + let parser = [{ + mlir::FunctionType type; + llvm::SmallVector operands; + if (parser.parseOperandList(operands, + mlir::OpAsmParser::Delimiter::None) || + 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.printOperands(triples()); + p << " : "; + p.printFunctionalType(getOperation()); + }]; +} + +def fir_InsertValueOp : fir_OneResultOp<"insert_value", [NoSideEffect]>, + Arguments<(ins AnyCompositeLike:$adt, AnyType:$val, + Variadic:$coor)> { + let summary = "insert a new sub-value into a copy of an existing aggregate"; + + let description = [{ + Insert a value into a composite value. + }]; + + let parser = [{ + mlir::FunctionType type; + llvm::SmallVector operands; + if (parser.parseOperandList(operands, + mlir::OpAsmParser::Delimiter::None) || + 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(adt()); + p << ", "; + p.printOperands(coor()); + p << " : "; + p.printFunctionalType(getOperation()); + }]; +} + +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 field (offset) value from an LEN parameter 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. + }]; + + let arguments = (ins StrAttr:$field_id); + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, StringRef fieldName", + [{ + result.addAttribute("name", builder->getStringAttr(fieldName)); + }] + >]; + + let parser = [{ + mlir::StringAttr attr; + mlir::Type type; + if (parser.parseLParen() || + parser.parseAttribute(attr, "field_id", result.attributes) || + parser.parseRParen() || + parser.parseColonType(type) || + parser.addTypeToList(type, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << " (" << getAttr("field_id") << ") : " + << getType(); + }]; +} + +// Fortran loops + +def ImplicitFirTerminator : SingleBlockImplicitTerminator<"FirEndOp">; + +def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> { + let summary = "generalized loop operation"; + let description = [{ + A generalized Fortran loop construct. + }]; + let arguments = (ins Index:$lowerBound, Index:$upperBound, + Variadic:$optstep, OptionalAttr:$constep, + OptionalAttr:$unordered); + let regions = (region SizedRegion<1>:$region); + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<"mlir::Builder *builder, mlir::OperationState &result," + "int64_t lowerBound, int64_t upperBound, int64_t step = 1">, + OpBuilder<"mlir::Builder *builder, mlir::OperationState &result," + "mlir::Value *lowerBound, mlir::Value *upperBound," + "llvm::ArrayRef step"> + ]; + + let printer = [{ + p << getOperationName() << ' ' << *getInductionVar() << " = " + << *lowerBound() << " to " << *upperBound(); + auto s = optstep(); + if (s.begin() != s.end()) { + p << " step "; + p.printOperands(s.begin(), s.end()); + } + if (unordered()) + p << " unordered"; + p.printRegion(region(), /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/false); + p.printOptionalAttrDict(getAttrs(), {"unordered", "step"}); + }]; + let verifier = [{ + auto step = optstep(); + if (step.begin() != step.end()) { + 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"); + return mlir::success(); + }]; + let parser = [{ return parseLoopOp(parser, result); }]; + + let extraClassDeclaration = [{ + mlir::Block *getBody() { return ®ion().front(); } + mlir::Value *getInductionVar() { return getBody()->getArgument(0); } + 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); + } + static char const* getStepKeyword() { + return "step"; + } + }]; +} + +def fir_WhereOp : fir_Op<"where", [ImplicitFirTerminator]> { + let summary = "generalized conditional operation"; + let description = [{ + This is a generalized conditional construct. + }]; + 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 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 parser = [{ return parseWhereOp(parser, result); }]; + + 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", []>, + Results<(outs Variadic)>, + Arguments<(ins SymbolRefAttr:$proc, Variadic:$args)> { + let summary = "call a procedure directly"; + + let description = [{ + Provides a custom parser and pretty printer to allow for a slightly more + readable syntax in the FIR dialect, e.g. `fir.call @sub(%12)`. + }]; + + let parser = [{ + M::FunctionType calleeType; + M::StringAttr proc; + L::SmallVector operands; + if (parser.parseAttribute(proc, "proc", result.attributes) || + parser.parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || + parser.parseOptionalAttributeDict(result.attributes) || + parser.parseColonType(calleeType) || + parser.resolveOperands(operands, calleeType.getInputs(), + parser.getNameLoc(), result.operands) || + parser.addTypesToList(calleeType.getResults(), result.types)) + return M::failure(); + return M::success(); + }]; + let printer = [{ + p << getOperationName() << ' ' << getAttr("proc") << '('; + p.printOperands(args()); + p << ')'; + p.printOptionalAttrDict(getAttrs(), {"proc"}); + p << " : "; + p.printFunctionalType(getOperation()); + }]; +} + +def fir_ICallOp : fir_Op<"icall", []>, + Results<(outs Variadic)>, + Arguments<(ins FunctionType:$proc, Variadic:$args)> { + let summary = "call a procedure indirectly"; + + let description = [{ + Provides a custom parser and pretty printer to allow for a slightly more + readable syntax in the FIR dialect, e.g. `fir.icall %fun(%12)`. + }]; + + let parser = [{ + M::FunctionType calleeType; + L::SmallVector operands; + M::OpAsmParser::OperandType callee; + if (parser.parseOperand(callee)) + return M::failure(); + operands.push_back(callee); + auto calleeLoc = parser.getNameLoc(); + if (parser.parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || + parser.parseOptionalAttributeDict(result.attributes) || + parser.parseColonType(calleeType) || + parser.addTypesToList(calleeType.getResults(), result.types)) + return M::failure(); + L::SmallVector types; + types.push_back(calleeType); + auto input = calleeType.getInputs(); + types.insert(types.end(), input.begin(), input.end()); + if (parser.resolveOperands(operands, types, calleeLoc, result.operands)) + return M::failure(); + return M::success(); + }]; + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(proc()); + p << '('; + p.printOperands(args()); + p << ')'; + p.printOptionalAttrDict(getAttrs(), {"proc"}); + p << " : " << getCallee()->getType(); + }]; + let extraClassDeclaration = [{ + Value *getCallee() { return getOperand(0); } + + operand_range getArgOperands() { + return {arg_operand_begin(), arg_operand_end()}; + } + + operand_iterator arg_operand_begin() { return ++operand_begin(); } + operand_iterator arg_operand_end() { return operand_end(); } + }]; +} + +def fir_DispatchOp : fir_Op<"dispatch", []>, + Results<(outs Variadic)>, + Arguments<(ins StrAttr:$method, fir_BoxType:$object, + Variadic:$args)> { + let summary = "call a type-bound procedure"; + + let description = [{ + Dynamic dispatch to the specified method. + }]; + + let parser = [{ + M::FunctionType calleeType; + L::SmallVector operands; + auto calleeLoc = parser.getNameLoc(); + M::StringAttr calleeAttr; + if (parser.parseAttribute(calleeAttr, "method", result.attributes) || + parser.parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || + parser.parseOptionalAttributeDict(result.attributes) || + parser.parseColonType(calleeType) || + parser.addTypesToList(calleeType.getResults(), result.types) || + parser.resolveOperands( + operands, calleeType.getInputs(), calleeLoc, result.operands)) + return M::failure(); + return M::success(); + }]; + let printer = [{ + p << getAttr("method") << '('; + p.printOperand(object()); + if (arg_operand_begin() != arg_operand_end()) { + p << ", "; + p.printOperands(args()); + } + p << ')'; + p.printOptionalAttrDict(getAttrs(), {"method"}); + llvm::SmallVector resTy(getResultTypes()); + llvm::SmallVector argTy(getOperandTypes()); + p << " : " << M::FunctionType::get(argTy, resTy, getContext()); + }]; + let extraClassDeclaration = [{ + 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(); } + }]; +} + +// 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. + }]; + + 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.parseColonType(type) || + parser.addTypeToList(type, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + + let printer = [{ + p << getOperationName() << " (" << getAttr("symbol") << ") : " + << getType(); + }]; +} + +def fir_ConvertOp : fir_OneResultOp<"convert", [NoSideEffect]>, + Arguments<(ins AnyType:$value)> { + let summary = "encapsulates all Fortran scalar type conversions"; + + let description = [{ + Generalized type conversion. Not all pairs of types have conversions. + }]; + + let parser = [{ + mlir::FunctionType type; + llvm::SmallVector operands; + if (parser.parseOperandList(operands, 1, + mlir::OpAsmParser::Delimiter::None) || + 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(value()); + p << " : "; + p.printFunctionalType(getOperation()); + }]; +} + +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`. + }]; + + let parser = [{ + mlir::Type intype; + if (parser.parseType(intype)) + return mlir::failure(); + auto &builder = parser.getBuilder(); + result.addAttribute("in_type", builder.getTypeAttr(intype)); + mlir::Type restype; + if (parser.parseColonType(restype) || + parser.addTypeToList(restype, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + let printer = [{ + p << getOperationName() << ' ' << getAttr("in_type"); + p << " : " << getType(); + p.printOptionalAttrDict(getAttrs(), {"in_type"}); + }]; +} + +def fir_NoReassocOp : fir_OneResultOp<"no_reassoc", + [SameOperandsAndResultType]>, Arguments<(ins fir_Type:$val)> { + let summary = "synthetic op to prevent reassociation"; + + let description = [{ + The operation is to make sure that the Fortran optimizer does not + reassociate operations when they are syntactically surrounded by + parenthesis. + }]; + + let parser = [{ + mlir::Type type; + mlir::OpAsmParser::OperandType opnd; + if (parser.parseOperand(opnd) || + parser.parseColonType(type) || + parser.resolveOperand(opnd, type, result.operands) || + parser.addTypeToList(type, result.types)) + return mlir::failure(); + return mlir::success(); + }]; + let printer = [{ + p << getOperationName() << ' '; + p.printOperand(val()); + p << " : " << getType(); + }]; +} + +def fir_GlobalEntryOp : fir_Op<"global_entry", []>, + Arguments<(ins StrAttr:$field, AnyType:$constant)> { + let summary = "map entry in a global"; + + let description = [{ + An entry in a global. Allows a constant to be bound to a specific + field in a record even though the layout of the record has not yet + been reified. + }]; + + let parser = [{ + M::Type type; + M::StringAttr fieldAttr; + M::OpAsmParser::OperandType constant; + if (parser.parseAttribute(fieldAttr, "field", result.attributes) || + parser.parseComma() || + parser.parseOperand(constant) || + parser.parseColonType(type) || + parser.resolveOperand(constant, type, result.operands)) + return M::failure(); + result.addAttribute("in_type", parser.getBuilder().getTypeAttr(type)); + return M::success(); + }]; + let printer = [{ + p << getOperationName() << ' ' << getAttr("field") << ", "; + p.printOperand(constant()); + p << " : " << getAttr("in_type"); + }]; +} + +def fir_DTEntryOp : fir_Op<"dt_entry", []>, + Arguments<(ins StrAttr:$method, SymbolRefAttr:$proc)> { + 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. + }]; + + let parser = [{ + M::SymbolRefAttr calleeAttr; + M::StringAttr methodAttr; + if (parser.parseAttribute(methodAttr, "method", result.attributes) || + parser.parseComma() || + parser.parseAttribute(calleeAttr, "proc", result.attributes)) + return M::failure(); + return M::success(); + }]; + let printer = [{ + p << getOperationName() << ' ' << getAttr("method") << ", " + << getAttr("proc"); + }]; +} + +#endif diff --git a/include/fir/Transforms/MemToReg.h b/include/fir/Transforms/MemToReg.h new file mode 100644 index 000000000000..67e060ac0bfd --- /dev/null +++ b/include/fir/Transforms/MemToReg.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIR_TRANSFORMS_MEMTOREG_H +#define FIR_TRANSFORMS_MEMTOREG_H + +/// A pass to convert the FIR dialect from "Mem-SSA" form to "Reg-SSA" +/// form. This pass is a port of LLVM's mem2reg pass, but modified for the FIR +/// dialect as well as the restructuring of MLIR's representation to present PHI +/// nodes as block arguments. + +#include + +namespace mlir { +template +class OpPassBase; +class FuncOp; +using FunctionPassBase = OpPassBase; +} // namespace mlir + +namespace fir { + +/// Creates a pass to convert FIR into a reg SSA form +std::unique_ptr createMemToRegPass(); + +} // namespace fir + +#endif // FIR_TRANSFORMS_MEMTOREG_H diff --git a/include/fir/Type.h b/include/fir/Type.h new file mode 100644 index 000000000000..0f423f4dbaf5 --- /dev/null +++ b/include/fir/Type.h @@ -0,0 +1,277 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIR_TYPE_H +#define FIR_TYPE_H + +#include "mlir/IR/Types.h" + +namespace llvm { +class StringRef; +template +class ArrayRef; +class hash_code; +} // namespace llvm + +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 LogicalTypeStorage; +struct PointerTypeStorage; +struct RealTypeStorage; +struct RecordTypeStorage; +struct ReferenceTypeStorage; +struct SequenceTypeStorage; +struct TypeDescTypeStorage; +} // namespace detail + +enum TypeKind { + // The enum starts at the range reserved for this dialect. + FIR_TYPE = mlir::Type::FIRST_FIR_TYPE, + FIR_BOX, + FIR_BOXCHAR, + FIR_BOXPROC, + FIR_CHARACTER, // intrinsic + FIR_COMPLEX, // intrinsic + FIR_DERIVED, // derived + FIR_DIMS, + FIR_FIELD, + FIR_HEAP, + FIR_INT, // intrinsic + FIR_LOGICAL, // intrinsic + FIR_POINTER, + FIR_REAL, // intrinsic + FIR_REFERENCE, + FIR_SEQUENCE, + FIR_TYPEDESC, +}; + +bool isa_fir_type(mlir::Type); +bool isa_std_type(mlir::Type t); +bool isa_fir_or_std_type(mlir::Type t); + +template +struct IntrinsicTypeMixin { + constexpr static bool kindof(unsigned kind) { return kind == getId(); } + constexpr static unsigned getId() { return Id; } +}; + +class CharacterType + : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static CharacterType get(mlir::MLIRContext *ctxt, KindTy kind); + int getSizeInBits() const; + KindTy getFKind() const { return getSizeInBits() / 8; } +}; + +class IntType + : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static IntType get(mlir::MLIRContext *ctxt, KindTy kind); + int getSizeInBits() const; + KindTy getFKind() const { return getSizeInBits() / 8; } +}; + +class LogicalType + : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static LogicalType get(mlir::MLIRContext *ctxt, KindTy kind); + int getSizeInBits() const; + KindTy getFKind() const { return getSizeInBits() / 8; } +}; + +class RealType : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static RealType get(mlir::MLIRContext *ctxt, KindTy kind); + int getSizeInBits() const; + KindTy getFKind() const { return getSizeInBits() / 8; } +}; + +class CplxType : public mlir::Type::TypeBase, + public IntrinsicTypeMixin { +public: + using Base::Base; + static CplxType get(mlir::MLIRContext *ctxt, KindTy kind); + int getSizeInBits() const; + KindTy getFKind() const; +}; + +// FIR support types + +class BoxType + : public mlir::Type::TypeBase { +public: + using Base::Base; + static BoxType get(mlir::Type eleTy); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_BOX; } + mlir::Type getEleTy() const; +}; + +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; +}; + +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; +}; + +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; +}; + +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; +}; + +class SequenceType : public mlir::Type::TypeBase { +public: + using Base::Base; + using BoundInfo = int64_t; + struct Extent { + bool known; + BoundInfo bound; + explicit Extent(bool k, BoundInfo b) : known(k), bound(b) {} + }; + using Bounds = std::vector; + struct Shape { + bool known; + Bounds bounds; + Shape() : known(false) {} + Shape(const Bounds &b) : known(true), bounds(b) {} + }; + + mlir::Type getEleTy() const; + Shape getShape() const; + + static SequenceType get(const Shape &shape, mlir::Type elementType); + static bool kindof(unsigned kind) { return kind == TypeKind::FIR_SEQUENCE; } +}; + +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 bool kindof(unsigned kind) { return kind == TypeKind::FIR_TYPEDESC; } + mlir::Type getOfTy() const; +}; + +// 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(); + + static RecordType get(mlir::MLIRContext *ctxt, llvm::StringRef name, + llvm::ArrayRef lenPList = {}, + llvm::ArrayRef typeList = {}); + constexpr static bool kindof(unsigned kind) { return kind == getId(); } + constexpr static unsigned getId() { return TypeKind::FIR_DERIVED; } +}; + +mlir::Type parseFirType(FIROpsDialect *dialect, llvm::StringRef rawData, + mlir::Location loc); + +} // namespace fir + +#endif // FIR_TYPE_H diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 343195bae2b0..5d4c630ea209 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -6,6 +6,7 @@ # #===------------------------------------------------------------------------===# +add_subdirectory(burnside) add_subdirectory(common) add_subdirectory(evaluate) add_subdirectory(decimal) diff --git a/lib/burnside/CMakeLists.txt b/lib/burnside/CMakeLists.txt new file mode 100644 index 000000000000..c9c99ae0315b --- /dev/null +++ b/lib/burnside/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") + +add_library(FortranBurnside + flattened.cc +) + +install (TARGETS FortranBurnside + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) diff --git a/lib/burnside/builder.h b/lib/burnside/builder.h new file mode 100644 index 000000000000..2e13a0f76153 --- /dev/null +++ b/lib/burnside/builder.h @@ -0,0 +1,78 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_BUILDER_H_ +#define FORTRAN_BURNSIDE_BUILDER_H_ + +#include "../semantics/symbol.h" +#include "llvm/ADT/DenseMap.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/Function.h" +#include "mlir/IR/Module.h" +#include + +namespace llvm { +class StringRef; +} + +namespace Fortran::burnside { + +/// Miscellaneous helper routines for building MLIR +/// [Coding style](https://llvm.org/docs/CodingStandards.html) + +class SymMap { + llvm::DenseMap symbolMap; + +public: + void addSymbol(const semantics::Symbol *symbol, mlir::Value *value); + + mlir::Value *lookupSymbol(const semantics::Symbol *symbol); +}; + +std::string applyNameMangling(llvm::StringRef parserName); + +/// Get the current Module +inline mlir::ModuleOp getModule(mlir::OpBuilder *bldr) { + return bldr->getBlock()->getParent()->getParentOfType(); +} + +/// Get the current Function +inline mlir::FuncOp getFunction(mlir::OpBuilder *bldr) { + return bldr->getBlock()->getParent()->getParentOfType(); +} + +/// Get the entry block of the current Function +inline mlir::Block *getEntryBlock(mlir::OpBuilder *bldr) { + return &getFunction(bldr).front(); +} + +/// Create a new basic block +inline mlir::Block *createBlock(mlir::OpBuilder *bldr, mlir::Region *region) { + return bldr->createBlock(region, region->end()); +} + +inline mlir::Block *createBlock(mlir::OpBuilder *bldr) { + return createBlock(bldr, bldr->getBlock()->getParent()); +} + +/// Get a function by name (or null) +mlir::FuncOp getNamedFunction(llvm::StringRef name); + +/// Create a new Function +mlir::FuncOp createFunction( + mlir::ModuleOp module, llvm::StringRef name, mlir::FunctionType funcTy); + +} // Fortran::burnside + +#endif // FORTRAN_BURNSIDE_BUILDER_H_ diff --git a/lib/burnside/flattened.cc b/lib/burnside/flattened.cc new file mode 100644 index 000000000000..387cf3f377d4 --- /dev/null +++ b/lib/burnside/flattened.cc @@ -0,0 +1,695 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "flattened.h" +#include "../parser/parse-tree-visitor.h" +#include "../semantics/symbol.h" + +namespace Fortran::burnside { +namespace flat { + +// Labels are numbered [0 .. `n`] consecutively. They are unsigned. Not all +// labels are numbered. The unnumbered ones are given the value UINT_MAX. `n` +// should never approach UINT_MAX. +LabelBuilder::LabelBuilder() : referenced(32), counter{0u} {} + +LabelRef LabelBuilder::getNext() { + LabelRef next{counter++}; + auto cap{referenced.size()}; + if (cap < counter) { + referenced.resize(2 * cap); + } + referenced.reset(next); + return next; +} + +void LabelBuilder::setReferenced(LabelRef label) { + CHECK(label < referenced.getBitCapacity()); + referenced.set(label); +} + +bool LabelBuilder::isReferenced(LabelRef label) const { + CHECK(label < referenced.getBitCapacity()); + return referenced.test(label); +} + +LabelOp::LabelOp(LabelBuilder &builder) + : builder{builder}, label{builder.getNext()} {} + +LabelOp::LabelOp(const LabelOp &that) + : builder{that.builder}, label{that.label} {} + +LabelOp &LabelOp::operator=(const LabelOp &that) { + CHECK(&builder == &that.builder); + label = that.label; + return *this; +} + +void LabelOp::setReferenced() const { builder.setReferenced(label); } + +bool LabelOp::isReferenced() const { return builder.isReferenced(label); } + +static void AddAssign(AnalysisData &ad, const semantics::Symbol *symbol, + const parser::Label &label) { + ad.assignMap[symbol].insert(label); +} + +std::vector GetAssign( + AnalysisData &ad, const semantics::Symbol *symbol) { + std::vector result; + for (auto lab : ad.assignMap[symbol]) { + result.emplace_back(lab); + } + return result; +} + +static std::tuple FindStack( + const std::vector> + &stack, + const parser::Name *key) { + for (auto iter{stack.rbegin()}, iend{stack.rend()}; iter != iend; ++iter) { + if (std::get<0>(*iter) == key) { + return *iter; + } + } + assert(false && "construct name not on stack"); + return {}; +} + +LabelOp FetchLabel(AnalysisData &ad, const parser::Label &label) { + auto iter{ad.labelMap.find(label)}; + if (iter == ad.labelMap.end()) { + LabelOp ll{ad.labelBuilder}; + ll.setReferenced(); + ad.labelMap.insert({label, ll}); + return ll; + } + return iter->second; +} + +static LabelOp BuildNewLabel(AnalysisData &ad) { + return LabelOp{ad.labelBuilder}; +} + +template parser::Label GetErr(const A &stmt) { + if constexpr (std::is_same_v || + std::is_same_v) { + for (const auto &control : stmt.controls) { + if (std::holds_alternative(control.u)) { + return std::get(control.u).v; + } + } + } + if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) { + for (const auto &spec : stmt.v) { + if (std::holds_alternative(spec.u)) { + return std::get(spec.u).v; + } + } + } + if constexpr (std::is_same_v) { + for (const auto &spec : std::get>(stmt.u)) { + if (std::holds_alternative(spec.u)) { + return std::get(spec.u).v; + } + } + } + return 0; +} + +template parser::Label GetEor(const A &stmt) { + if constexpr (std::is_same_v || + std::is_same_v) { + for (const auto &control : stmt.controls) { + if (std::holds_alternative(control.u)) { + return std::get(control.u).v; + } + } + } + if constexpr (std::is_same_v) { + for (const auto &waitSpec : stmt.v) { + if (std::holds_alternative(waitSpec.u)) { + return std::get(waitSpec.u).v; + } + } + } + return 0; +} + +template parser::Label GetEnd(const A &stmt) { + if constexpr (std::is_same_v || + std::is_same_v) { + for (const auto &control : stmt.controls) { + if (std::holds_alternative(control.u)) { + return std::get(control.u).v; + } + } + } + if constexpr (std::is_same_v) { + for (const auto &waitSpec : stmt.v) { + if (std::holds_alternative(waitSpec.u)) { + return std::get(waitSpec.u).v; + } + } + } + return 0; +} + +template +void errLabelSpec(const A &s, std::list &ops, + const parser::Statement &ec, AnalysisData &ad) { + if (auto errLab{GetErr(s)}) { + std::optional errRef{FetchLabel(ad, errLab).get()}; + LabelOp next{BuildNewLabel(ad)}; + ops.emplace_back(SwitchIOOp{s, next, ec.source, errRef}); + ops.emplace_back(next); + } else { + ops.emplace_back(ActionOp{ec}); + } +} + +template +void threeLabelSpec(const A &s, std::list &ops, + const parser::Statement &ec, AnalysisData &ad) { + auto errLab{GetErr(s)}; + auto eorLab{GetEor(s)}; + auto endLab{GetEnd(s)}; + if (errLab || eorLab || endLab) { + std::optional errRef; + if (errLab) { + errRef = FetchLabel(ad, errLab).get(); + } + std::optional eorRef; + if (eorLab) { + eorRef = FetchLabel(ad, eorLab).get(); + } + std::optional endRef; + if (endLab) { + endRef = FetchLabel(ad, endLab).get(); + } + auto next{BuildNewLabel(ad)}; + ops.emplace_back(SwitchIOOp{s, next, ec.source, errRef, eorRef, endRef}); + ops.emplace_back(next); + } else { + ops.emplace_back(ActionOp{ec}); + } +} + +template +std::vector toLabelRef(AnalysisData &ad, const A &labels) { + std::vector result; + for (auto label : labels) { + result.emplace_back(FetchLabel(ad, label).get()); + } + CHECK(result.size() == labels.size()); + return result; +} + +template +std::vector toLabelRef( + const LabelOp &next, AnalysisData &ad, const A &labels) { + std::vector result; + result.emplace_back(next); + auto refs{toLabelRef(ad, labels)}; + result.insert(result.end(), refs.begin(), refs.end()); + CHECK(result.size() == labels.size() + 1); + return result; +} + +static bool hasAltReturns(const parser::CallStmt &callStmt) { + const auto &args{std::get>(callStmt.v.t)}; + for (const auto &arg : args) { + const auto &actual{std::get(arg.t)}; + if (std::holds_alternative(actual.u)) { + return true; + } + } + return false; +} + +static std::list getAltReturnLabels(const parser::Call &call) { + std::list result; + const auto &args{std::get>(call.t)}; + for (const auto &arg : args) { + const auto &actual{std::get(arg.t)}; + if (const auto *p{std::get_if(&actual.u)}) { + result.push_back(p->v); + } + } + return result; +} + +static LabelRef NearestEnclosingDoConstruct(AnalysisData &ad) { + for (auto iterator{ad.constructContextStack.rbegin()}, + endIterator{ad.constructContextStack.rend()}; + iterator != endIterator; ++iterator) { + auto labelReference{std::get<2>(*iterator)}; + if (labelReference != UnspecifiedLabel) { + return labelReference; + } + } + assert(false && "CYCLE|EXIT not in loop"); + return UnspecifiedLabel; +} + +template std::string GetSource(const A *s) { + return s->source.ToString(); +} + +template std::string GetSource(const B *s) { + return GetSource(&std::get>(s->t)); +} + +void Op::Build(std::list &ops, + const parser::Statement &ec, AnalysisData &ad) { + std::visit( + common::visitors{ + [&](const auto &) { ops.emplace_back(ActionOp{ec}); }, + [&](const common::Indirection &s) { + if (hasAltReturns(s.value())) { + auto next{BuildNewLabel(ad)}; + auto alts{getAltReturnLabels(s.value().v)}; + auto labels{toLabelRef(next, ad, alts)}; + ops.emplace_back( + SwitchOp{s.value(), std::move(labels), ec.source}); + ops.emplace_back(next); + } else { + ops.emplace_back(ActionOp{ec}); + } + }, + [&](const common::Indirection &s) { + AddAssign(ad, std::get(s.value().t).symbol, + std::get(s.value().t)); + ops.emplace_back(ActionOp{ec}); + }, + [&](const common::Indirection &s) { + ops.emplace_back(GotoOp{s.value(), + s.value().v ? std::get<2>(FindStack(ad.constructContextStack, + &s.value().v.value())) + : NearestEnclosingDoConstruct(ad), + ec.source}); + }, + [&](const common::Indirection &s) { + ops.emplace_back(GotoOp{s.value(), + s.value().v ? std::get<1>(FindStack(ad.constructContextStack, + &s.value().v.value())) + : NearestEnclosingDoConstruct(ad), + ec.source}); + }, + [&](const common::Indirection &s) { + ops.emplace_back(GotoOp{ + s.value(), FetchLabel(ad, s.value().v).get(), ec.source}); + }, + [&](const parser::FailImageStmt &s) { + ops.emplace_back(ReturnOp{s, ec.source}); + }, + [&](const common::Indirection &s) { + ops.emplace_back(ReturnOp{s.value(), ec.source}); + }, + [&](const common::Indirection &s) { + ops.emplace_back(ActionOp{ec}); + ops.emplace_back(ReturnOp{s.value(), ec.source}); + }, + [&](const common::Indirection &s) { + threeLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + threeLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + threeLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + errLabelSpec(s.value(), ops, ec, ad); + }, + [&](const common::Indirection &s) { + auto next{BuildNewLabel(ad)}; + auto labels{toLabelRef( + next, ad, std::get>(s.value().t))}; + ops.emplace_back(SwitchOp{s.value(), std::move(labels), ec.source}); + ops.emplace_back(next); + }, + [&](const common::Indirection &s) { + ops.emplace_back(SwitchOp{s.value(), + toLabelRef(ad, + std::list{std::get<1>(s.value().t), + std::get<2>(s.value().t), std::get<3>(s.value().t)}), + ec.source}); + }, + [&](const common::Indirection &s) { + ops.emplace_back( + IndirectGotoOp{std::get(s.value().t).symbol, + toLabelRef( + ad, std::get>(s.value().t))}); + }, + [&](const common::Indirection &s) { + auto then{BuildNewLabel(ad)}; + auto endif{BuildNewLabel(ad)}; + ops.emplace_back(ConditionalGotoOp{s.value(), then, endif}); + ops.emplace_back(then); + ops.emplace_back(ActionOp{ec}); + ops.emplace_back(endif); + }, + }, + ec.statement.u); +} + +template struct ElementMap; +template<> struct ElementMap { + using type = parser::CaseConstruct::Case; +}; +template<> struct ElementMap { + using type = parser::SelectRankConstruct::RankCase; +}; +template<> struct ElementMap { + using type = parser::SelectTypeConstruct::TypeCase; +}; + +struct ControlFlowAnalyzer { + explicit ControlFlowAnalyzer(std::list &ops, AnalysisData &ad) + : linearOps{ops}, ad{ad} {} + + LabelOp buildNewLabel() { return BuildNewLabel(ad); } + + Op findLabel(const parser::Label &lab) { + auto iter{ad.labelMap.find(lab)}; + if (iter == ad.labelMap.end()) { + LabelOp ll{ad.labelBuilder}; + ad.labelMap.insert({lab, ll}); + return {ll}; + } + return {iter->second}; + } + + template constexpr bool Pre(const A &) { return true; } + template constexpr void Post(const A &) {} + + template bool Pre(const parser::Statement &stmt) { + if (stmt.label) { + linearOps.emplace_back(findLabel(*stmt.label)); + } + if constexpr (std::is_same_v) { + Op::Build(linearOps, stmt, ad); + } + return true; + } + template + void appendIfLabeled(const parser::Statement &stmt, std::list &ops) { + if (stmt.label) { + ops.emplace_back(findLabel(*stmt.label)); + } + } + + // named constructs + template bool linearConstruct(const A &construct) { + std::list ops; + LabelOp label{buildNewLabel()}; + const parser::Name *name{getName(construct)}; + ad.constructContextStack.emplace_back( + name, GetLabelRef(label), UnspecifiedLabel); + appendIfLabeled(std::get<0>(construct.t), ops); + ops.emplace_back(BeginOp{construct}); + ControlFlowAnalyzer cfa{ops, ad}; + Walk(std::get(construct.t), cfa); + ops.emplace_back(label); + appendIfLabeled(std::get<2>(construct.t), ops); + ops.emplace_back(EndOp{construct}); + linearOps.splice(linearOps.end(), ops); + ad.constructContextStack.pop_back(); + return false; + } + + bool Pre(const parser::AssociateConstruct &c) { return linearConstruct(c); } + bool Pre(const parser::ChangeTeamConstruct &c) { return linearConstruct(c); } + bool Pre(const parser::CriticalConstruct &c) { return linearConstruct(c); } + + bool Pre(const parser::BlockConstruct &construct) { + std::list ops; + LabelOp label{buildNewLabel()}; + const auto &optName{ + std::get>(construct.t) + .statement.v}; + const parser::Name *name{optName ? &*optName : nullptr}; + ad.constructContextStack.emplace_back( + name, GetLabelRef(label), UnspecifiedLabel); + appendIfLabeled( + std::get>(construct.t), ops); + ops.emplace_back(BeginOp{construct}); + ControlFlowAnalyzer cfa{ops, ad}; + Walk(std::get(construct.t), cfa); + appendIfLabeled( + std::get>(construct.t), ops); + ops.emplace_back(EndOp{construct}); + ops.emplace_back(label); + linearOps.splice(linearOps.end(), ops); + ad.constructContextStack.pop_back(); + return false; + } + + /// `DO` constructs can be lowered to `fir.loop` if they meet some + /// constraints, otherwise they are lowered to a CFG. + bool Pre(const parser::DoConstruct &construct) { + std::list ops; + LabelOp backedgeLab{buildNewLabel()}; + LabelOp incrementLab{buildNewLabel()}; + LabelOp entryLab{buildNewLabel()}; + LabelOp exitLab{buildNewLabel()}; + const parser::Name *name{getName(construct)}; + LabelRef exitOpRef{GetLabelRef(exitLab)}; + ad.constructContextStack.emplace_back( + name, exitOpRef, GetLabelRef(incrementLab)); + appendIfLabeled( + std::get>(construct.t), ops); + ops.emplace_back(BeginOp{construct}); + ops.emplace_back(GotoOp{GetLabelRef(backedgeLab)}); + ops.emplace_back(incrementLab); + ops.emplace_back(DoIncrementOp{construct}); + ops.emplace_back(backedgeLab); + ops.emplace_back(DoCompareOp{construct}); + ops.emplace_back(ConditionalGotoOp{ + std::get>(construct.t), + GetLabelRef(entryLab), exitOpRef}); + ops.push_back(entryLab); + ControlFlowAnalyzer cfa{ops, ad}; + Walk(std::get(construct.t), cfa); + appendIfLabeled( + std::get>(construct.t), ops); + ops.emplace_back(GotoOp{GetLabelRef(incrementLab)}); + ops.emplace_back(EndOp{construct}); + ops.emplace_back(exitLab); + linearOps.splice(linearOps.end(), ops); + ad.constructContextStack.pop_back(); + return false; + } + + /// `IF` constructs can be lowered to `fir.where` if they meet some + /// constraints, otherwise they are lowered to a CFG. + bool Pre(const parser::IfConstruct &construct) { + std::list ops; + LabelOp thenLab{buildNewLabel()}; + LabelOp elseLab{buildNewLabel()}; + LabelOp exitLab{buildNewLabel()}; + const parser::Name *name{getName(construct)}; + ad.constructContextStack.emplace_back( + name, GetLabelRef(exitLab), UnspecifiedLabel); + appendIfLabeled( + std::get>(construct.t), ops); + ops.emplace_back(BeginOp{construct}); + ops.emplace_back(ConditionalGotoOp{ + std::get>(construct.t), + GetLabelRef(thenLab), GetLabelRef(elseLab)}); + ops.emplace_back(thenLab); + ControlFlowAnalyzer cfa{ops, ad}; + Walk(std::get(construct.t), cfa); + LabelRef exitOpRef{GetLabelRef(exitLab)}; + ops.emplace_back(GotoOp{exitOpRef}); + for (const auto &elseIfBlock : + std::get>(construct.t)) { + appendIfLabeled( + std::get>(elseIfBlock.t), ops); + ops.emplace_back(elseLab); + LabelOp newThenLab{buildNewLabel()}; + LabelOp newElseLab{buildNewLabel()}; + ops.emplace_back(ConditionalGotoOp{ + std::get>(elseIfBlock.t), + GetLabelRef(newThenLab), GetLabelRef(newElseLab)}); + ops.emplace_back(newThenLab); + Walk(std::get(elseIfBlock.t), cfa); + ops.emplace_back(GotoOp{exitOpRef}); + elseLab = newElseLab; + } + ops.emplace_back(elseLab); + if (const auto &optElseBlock{ + std::get>( + construct.t)}) { + appendIfLabeled( + std::get>(optElseBlock->t), ops); + Walk(std::get(optElseBlock->t), cfa); + } + ops.emplace_back(GotoOp{exitOpRef}); + ops.emplace_back(exitLab); + appendIfLabeled( + std::get>(construct.t), ops); + ops.emplace_back(EndOp{construct}); + linearOps.splice(linearOps.end(), ops); + ad.constructContextStack.pop_back(); + return false; + } + + template bool Multiway(const A &construct) { + using B = typename ElementMap::type; + std::list ops; + LabelOp exitLab{buildNewLabel()}; + const parser::Name *name{getName(construct)}; + ad.constructContextStack.emplace_back( + name, GetLabelRef(exitLab), UnspecifiedLabel); + appendIfLabeled(std::get<0>(construct.t), ops); + ops.emplace_back(BeginOp{construct}); + const auto N{std::get>(construct.t).size()}; + LabelRef exitOpRef{GetLabelRef(exitLab)}; + if (N > 0) { + typename std::list::size_type i; + std::vector toLabels; + for (i = 0; i != N; ++i) { + toLabels.emplace_back(buildNewLabel()); + } + std::vector targets; + for (i = 0; i != N; ++i) { + targets.emplace_back(GetLabelRef(toLabels[i])); + } + ops.emplace_back( + SwitchOp{construct, targets, std::get<0>(construct.t).source}); + ControlFlowAnalyzer cfa{ops, ad}; + i = 0; + for (const auto &caseBlock : std::get>(construct.t)) { + ops.emplace_back(toLabels[i++]); + appendIfLabeled(std::get<0>(caseBlock.t), ops); + Walk(std::get(caseBlock.t), cfa); + ops.emplace_back(GotoOp{exitOpRef}); + } + } + ops.emplace_back(exitLab); + appendIfLabeled(std::get<2>(construct.t), ops); + ops.emplace_back(EndOp{construct}); + linearOps.splice(linearOps.end(), ops); + ad.constructContextStack.pop_back(); + return false; + } + + bool Pre(const parser::CaseConstruct &c) { return Multiway(c); } + bool Pre(const parser::SelectRankConstruct &c) { return Multiway(c); } + bool Pre(const parser::SelectTypeConstruct &c) { return Multiway(c); } + + bool Pre(const parser::WhereConstruct &c) { + std::list ops; + LabelOp label{buildNewLabel()}; + const parser::Name *name{getName(c)}; + ad.constructContextStack.emplace_back( + name, GetLabelRef(label), UnspecifiedLabel); + appendIfLabeled( + std::get>(c.t), ops); + ops.emplace_back(BeginOp{c}); + ControlFlowAnalyzer cfa{ops, ad}; + Walk(std::get>(c.t), cfa); + Walk( + std::get>(c.t), cfa); + Walk(std::get>(c.t), cfa); + ops.emplace_back(label); + appendIfLabeled( + std::get>(c.t), ops); + ops.emplace_back(EndOp{c}); + linearOps.splice(linearOps.end(), ops); + ad.constructContextStack.pop_back(); + return false; + } + + bool Pre(const parser::ForallConstruct &construct) { + std::list ops; + LabelOp label{buildNewLabel()}; + const parser::Name *name{getName(construct)}; + ad.constructContextStack.emplace_back( + name, GetLabelRef(label), UnspecifiedLabel); + appendIfLabeled( + std::get>(construct.t), + ops); + ops.emplace_back(BeginOp{construct}); + ControlFlowAnalyzer cfa{ops, ad}; + Walk(std::get>(construct.t), cfa); + ops.emplace_back(label); + appendIfLabeled( + std::get>(construct.t), ops); + ops.emplace_back(EndOp{construct}); + linearOps.splice(linearOps.end(), ops); + ad.constructContextStack.pop_back(); + return false; + } + + template const parser::Name *getName(const A &a) { + const auto &optName{std::get<0>(std::get<0>(a.t).statement.t)}; + return optName ? &*optName : nullptr; + } + + LabelRef GetLabelRef(const LabelOp &label) { + label.setReferenced(); + return label; + } + + LabelRef GetLabelRef(const parser::Label &label) { + return FetchLabel(ad, label); + } + + std::list &linearOps; + AnalysisData &ad; +}; + +} // namespace flat + +template +void CreateFlatIR(const A &ptree, std::list &ops, AnalysisData &ad) { + flat::ControlFlowAnalyzer linearize{ops, ad}; + Walk(ptree, linearize); +} + +#define INSTANTIATE_EXPLICITLY(T) \ + template void CreateFlatIR( \ + const parser::T &, std::list &, AnalysisData &) +INSTANTIATE_EXPLICITLY(MainProgram); +INSTANTIATE_EXPLICITLY(FunctionSubprogram); +INSTANTIATE_EXPLICITLY(SubroutineSubprogram); + +} // namespace burnside diff --git a/lib/burnside/flattened.h b/lib/burnside/flattened.h new file mode 100644 index 000000000000..7d07d477968f --- /dev/null +++ b/lib/burnside/flattened.h @@ -0,0 +1,237 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_FLATTENED_H_ +#define FORTRAN_BURNSIDE_FLATTENED_H_ + +#include "mixin.h" +#include "../parser/parse-tree.h" +#include "llvm/ADT/BitVector.h" +#include +#include +#include +#include +#include + +namespace Fortran::burnside { + +struct AnalysisData; + +namespace flat { + +/// This is a flattened, linearized representation of the parse +/// tree. It captures the executable specification of the input +/// program. The flattened IR can be used to construct FIR. +/// +/// [Coding style](https://llvm.org/docs/CodingStandards.html) + +using LabelRef = unsigned; +constexpr LabelRef UnspecifiedLabel{UINT_MAX}; + +using Location = parser::CharBlock; +struct LabelBuilder; + +// target for a control-flow edge +struct LabelOp { + explicit LabelOp(LabelBuilder &builder); + LabelOp(const LabelOp &that); + LabelOp &operator=(const LabelOp &that); + void setReferenced() const; + bool isReferenced() const; + LabelRef get() const { return label; } + operator LabelRef() const { return get(); } + +private: + LabelBuilder &builder; + LabelRef label; +}; + +struct ArtificialJump {}; + +// a source of an absolute control flow edge +struct GotoOp + : public SumTypeCopyMixin { + template + explicit GotoOp(const A &stmt, LabelRef dest, const Location &source) + : SumTypeCopyMixin{&stmt}, target{dest}, source{source} {} + explicit GotoOp(LabelRef dest) + : SumTypeCopyMixin{ArtificialJump{}}, target{dest} {} + + LabelRef target; + Location source; +}; + +// control exits the procedure +struct ReturnOp : public SumTypeCopyMixin { + template + explicit ReturnOp(const A &stmt, const Location &source) + : SumTypeCopyMixin{&stmt}, source{source} {} + + Location source; +}; + +// two-way branch based on a condition +struct ConditionalGotoOp + : public SumTypeCopyMixin *, + const parser::Statement *, const parser::IfStmt *, + const parser::Statement *> { + template + explicit ConditionalGotoOp(const A &cond, LabelRef tb, LabelRef fb) + : SumTypeCopyMixin{&cond}, trueLabel{tb}, falseLabel{fb} {} + + LabelRef trueLabel; + LabelRef falseLabel; +}; + +// multi-way branch based on a target-value of a variable +struct IndirectGotoOp { + explicit IndirectGotoOp( + const semantics::Symbol *symbol, std::vector &&labelRefs) + : labelRefs{labelRefs}, symbol{symbol} {} + + std::vector labelRefs; + const semantics::Symbol *symbol; +}; + +// intrinsic IO operations can return with an implied multi-way branch +struct SwitchIOOp + : public SumTypeCopyMixin { + template + explicit SwitchIOOp(const A &io, LabelRef next, const Location &source, + std::optional errLab, + std::optional eorLab = std::nullopt, + std::optional endLab = std::nullopt) + : SumTypeCopyMixin{&io}, next{next}, source{source}, errLabel{errLab}, + eorLabel{eorLab}, endLabel{endLab} {} + LabelRef next; + Location source; + std::optional errLabel; + std::optional eorLabel; + std::optional endLabel; +}; + +// multi-way branch based on conditions +struct SwitchOp + : public SumTypeCopyMixin { + template + explicit SwitchOp( + const A &sw, const std::vector &refs, const Location &source) + : SumTypeCopyMixin{&sw}, refs{refs}, source{source} {} + + const std::vector refs; + Location source; +}; + +// a compute step +struct ActionOp { + explicit ActionOp(const parser::Statement &stmt) + : v{&stmt} {} + + const parser::Statement *v; +}; + +#define CONSTRUCT_TYPES \ + const parser::AssociateConstruct *, const parser::BlockConstruct *, \ + const parser::CaseConstruct *, const parser::ChangeTeamConstruct *, \ + const parser::CriticalConstruct *, const parser::DoConstruct *, \ + const parser::IfConstruct *, const parser::SelectRankConstruct *, \ + const parser::SelectTypeConstruct *, const parser::WhereConstruct *, \ + const parser::ForallConstruct *, const parser::CompilerDirective *, \ + const parser::OpenMPConstruct *, const parser::OmpEndLoopDirective * + +// entry into a Fortran construct +struct BeginOp : public SumTypeCopyMixin { + SUM_TYPE_COPY_MIXIN(BeginOp) + + template explicit BeginOp(const A &c) : SumTypeCopyMixin{&c} {} +}; + +// exit from a Fortran construct +struct EndOp : public SumTypeCopyMixin { + SUM_TYPE_COPY_MIXIN(EndOp) + + template explicit EndOp(const A &c) : SumTypeCopyMixin{&c} {} +}; + +struct DoIncrementOp { + explicit DoIncrementOp(const parser::DoConstruct &stmt) : v{&stmt} {} + + const parser::DoConstruct *v; +}; + +struct DoCompareOp { + DoCompareOp(const parser::DoConstruct &stmt) : v{&stmt} {} + + const parser::DoConstruct *v; +}; + +// the flat structure is a list of Ops, where an Op is any of ... +struct Op : public SumTypeMixin { + template Op(const A &thing) : SumTypeMixin{thing} {} + + static void Build(std::list &ops, + const parser::Statement &ec, AnalysisData &ad); +}; + +// helper to build unique labels +struct LabelBuilder { + LabelBuilder(); + LabelRef getNext(); + void setReferenced(LabelRef label); + bool isReferenced(LabelRef label) const; + llvm::BitVector referenced; + unsigned counter; +}; + +LabelOp FetchLabel(AnalysisData &ad, const parser::Label &label); + +std::vector GetAssign( + AnalysisData &ad, const semantics::Symbol *symbol); + +} // namespace flat + +// Collection of data maintained internally by the flattening algorithm +struct AnalysisData { + std::map labelMap; + std::vector> + constructContextStack; + flat::LabelBuilder labelBuilder; + std::map> assignMap; +}; + +// entry-point into building the flat IR +template +void CreateFlatIR(const A &ptree, std::list &ops, AnalysisData &ad); + +#define EXPLICIT_INSTANTIATION(T) \ + extern template void CreateFlatIR( \ + const parser::T &, std::list &, AnalysisData &) +EXPLICIT_INSTANTIATION(MainProgram); +EXPLICIT_INSTANTIATION(FunctionSubprogram); +EXPLICIT_INSTANTIATION(SubroutineSubprogram); + +} // namespace burnside + +#endif // FORTRAN_BURNSIDE_FLATTENED_H_ diff --git a/lib/burnside/mixin.h b/lib/burnside/mixin.h new file mode 100644 index 000000000000..f24ccc2b1b08 --- /dev/null +++ b/lib/burnside/mixin.h @@ -0,0 +1,72 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_MIXIN_H_ +#define FORTRAN_BURNSIDE_MIXIN_H_ + +// Mixin classes are "partial" classes (not used standalone) that can be used to +// add a repetitive (ad hoc) interface (and implementation) to a class. It's +// better to think of these as "included in" a class, rather than as an +// "inherited from" base class. + +/// [Coding style](https://llvm.org/docs/CodingStandards.html) + +#include "llvm/ADT/ilist.h" +#include +#include +#include +#include + +namespace Fortran::burnside { + +// implementation of a (moveable) sum type (variant) +template struct SumTypeMixin { + using SumTypeTrait = std::true_type; + template SumTypeMixin(const A &x) : u{x} {} + template SumTypeMixin(A &&x) : u{std::forward(x)} {} + SumTypeMixin(SumTypeMixin &&) = default; + SumTypeMixin &operator=(SumTypeMixin &&) = default; + SumTypeMixin(const SumTypeMixin &) = delete; + SumTypeMixin &operator=(const SumTypeMixin &) = delete; + SumTypeMixin() = delete; + std::variant u; +}; + +// implementation of a copyable sum type +template struct SumTypeCopyMixin { + using CopyableSumTypeTrait = std::true_type; + template SumTypeCopyMixin(const A &x) : u{x} {} + template SumTypeCopyMixin(A &&x) : u{std::forward(x)} {} + SumTypeCopyMixin(SumTypeCopyMixin &&) = default; + SumTypeCopyMixin &operator=(SumTypeCopyMixin &&) = default; + SumTypeCopyMixin(const SumTypeCopyMixin &) = default; + SumTypeCopyMixin &operator=(const SumTypeCopyMixin &) = default; + SumTypeCopyMixin() = delete; + std::variant u; +}; +#define SUM_TYPE_COPY_MIXIN(DT) \ + DT(const DT &derived) : SumTypeCopyMixin(derived.u) {} \ + DT(DT &&derived) : SumTypeCopyMixin(std::move(derived.u)) {} \ + DT &operator=(const DT &derived) { \ + SumTypeCopyMixin::operator=(derived.u); \ + return *this; \ + } \ + DT &operator=(DT &&derived) { \ + SumTypeCopyMixin::operator=(std::move(derived.u)); \ + return *this; \ + } + +} // namespace burnside + +#endif // FORTRAN_BURNSIDE_MIXIN_H_ From 9603b688498868b527e60ab335d64a7571a37508 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Mon, 1 Apr 2019 11:09:03 -0700 Subject: [PATCH 002/123] Burnside bridge implementation adding types - expand sequence type to support Fortran descriptor information add code to lower function type signatures to LLVM types add support and lowering for an if-then-else construct add support and lowering for SELECT X operations add address arithmetic to canonicalize changes to start adding new concepts to FIR start adding top-level special forms convert the build system to work as an in-tree build finish draft of pretty parsers and printers Conflicts: CMakeLists.txt --- .clang-format | 2 +- CMakeLists.txt | 555 +++++- cmake/modules/AddFlang.cmake | 140 ++ cmake/modules/CMakeLists.txt | 73 + cmake/modules/FlangConfig.cmake.in | 13 + docs/CMakeLists.txt | 92 + docs/doxygen.cfg.in | 2293 +++++++++++++++++++++++++ documentation/FIRLangRef.md | 1112 ++++++++++++ include/CMakeLists.txt | 2 + include/fir/CMakeLists.txt | 3 + include/fir/FIROps.td | 3 +- include/fir/Tilikum/CMakeLists.txt | 14 + include/fir/Tilikum/LLVMConverter.h | 37 + include/fir/Tilikum/StdConverter.h | 31 + include/fir/Transforms/CMakeLists.txt | 14 + include/flang/Version.h | 61 + include/flang/Version.inc.in | 5 + lib/CMakeLists.txt | 3 +- lib/burnside/CMakeLists.txt | 12 + lib/burnside/bridge.cc | 951 ++++++++++ lib/burnside/bridge.h | 93 + lib/burnside/builder.cc | 56 + lib/burnside/canonicalize.cc | 619 +++++++ lib/burnside/canonicalize.h | 58 + lib/burnside/common.h | 62 + lib/burnside/expression.cc | 405 +++++ lib/burnside/expression.h | 95 + lib/burnside/fe-helper.cc | 337 ++++ lib/burnside/fe-helper.h | 98 ++ lib/burnside/flattened.cc | 86 +- lib/burnside/flattened.h | 28 +- lib/burnside/mixin.h | 2 - lib/burnside/runtime.cc | 106 ++ lib/burnside/runtime.def | 42 + lib/burnside/runtime.h | 49 + lib/fir/Attribute.cpp | 178 ++ lib/fir/CMakeLists.txt | 45 + lib/fir/Dialect.cpp | 182 ++ lib/fir/FIROps.cpp | 378 ++++ lib/fir/LLVMConverter.cpp | 705 ++++++++ lib/fir/MemToReg.cpp | 760 ++++++++ lib/fir/StdConverter.cpp | 175 ++ lib/fir/Type.cpp | 1306 ++++++++++++++ test/fir/fir-ops.fir | 81 + tools/CMakeLists.txt | 1 + tools/f18/CMakeLists.txt | 18 +- tools/fml/CMakeLists.txt | 38 + tools/fml/fml.cc | 635 +++++++ 48 files changed, 11888 insertions(+), 166 deletions(-) create mode 100644 cmake/modules/AddFlang.cmake create mode 100644 cmake/modules/CMakeLists.txt create mode 100644 cmake/modules/FlangConfig.cmake.in create mode 100644 docs/CMakeLists.txt create mode 100644 docs/doxygen.cfg.in create mode 100644 documentation/FIRLangRef.md create mode 100644 include/CMakeLists.txt create mode 100644 include/fir/Tilikum/CMakeLists.txt create mode 100644 include/fir/Tilikum/LLVMConverter.h create mode 100644 include/fir/Tilikum/StdConverter.h create mode 100644 include/fir/Transforms/CMakeLists.txt create mode 100644 include/flang/Version.h create mode 100644 include/flang/Version.inc.in create mode 100644 lib/burnside/bridge.cc create mode 100644 lib/burnside/bridge.h create mode 100644 lib/burnside/builder.cc create mode 100644 lib/burnside/canonicalize.cc create mode 100644 lib/burnside/canonicalize.h create mode 100644 lib/burnside/common.h create mode 100644 lib/burnside/expression.cc create mode 100644 lib/burnside/expression.h create mode 100644 lib/burnside/fe-helper.cc create mode 100644 lib/burnside/fe-helper.h create mode 100644 lib/burnside/runtime.cc create mode 100644 lib/burnside/runtime.def create mode 100644 lib/burnside/runtime.h create mode 100644 lib/fir/Attribute.cpp create mode 100644 lib/fir/CMakeLists.txt create mode 100644 lib/fir/Dialect.cpp create mode 100644 lib/fir/FIROps.cpp create mode 100644 lib/fir/LLVMConverter.cpp create mode 100644 lib/fir/MemToReg.cpp create mode 100644 lib/fir/StdConverter.cpp create mode 100644 lib/fir/Type.cpp create mode 100644 test/fir/fir-ops.fir create mode 100644 tools/fml/CMakeLists.txt create mode 100644 tools/fml/fml.cc diff --git a/.clang-format b/.clang-format index eb459959d4d8..cea0a90a1407 100644 --- a/.clang-format +++ b/.clang-format @@ -16,7 +16,7 @@ FixNamespaceComments: false IncludeCategories: - Regex: '^<' Priority: 4 - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + - Regex: '^"(llvm|llvm-c|clang|clang-c|mlir|mlir-c)/' Priority: 3 - Regex: '^"\.\./' Priority: 2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 755ed6529dde..374a3e1afe33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,95 +8,347 @@ cmake_minimum_required(VERSION 3.9.0) -option(LINK_WITH_FIR "Link driver with FIR and LLVM" ON) - -# Pass -DGCC=... to cmake to use a specific gcc installation. -if( GCC ) - set(CMAKE_CXX_COMPILER "${GCC}/bin/g++") - set(CMAKE_CC_COMPILER "${GCC}/bin/gcc") - set(CMAKE_BUILD_RPATH "${GCC}/lib64") - set(CMAKE_INSTALL_RPATH "${GCC}/lib64") -endif() -if(BUILD_WITH_CLANG) - file(TO_CMAKE_PATH "${BUILD_WITH_CLANG}" CLANG_PATH) - set(CMAKE_CXX_COMPILER "${CLANG_PATH}/bin/clang++") - set(CMAKE_CC_COMPILER "${CLANG_PATH}/bin/clang") - if(GCC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --gcc-toolchain=${GCC}") - endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-command-line-argument") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wstring-conversion") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcovered-switch-default") +if (POLICY CMP0068) + cmake_policy(SET CMP0068 NEW) + set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR ON) endif() -# Set RPATH in every executable, overriding the default setting. -# If you set this first variable back to true (the default), -# also set the second one. -set(CMAKE_SKIP_BUILD_RPATH false) -set(CMAKE_BUILD_WITH_INSTALL_RPATH false) +if (POLICY CMP0077) + # FIXME??: Not yet certain this is the behavior we want... + cmake_policy(SET CMP0077 NEW) +endif() -set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib" "${CMAKE_INSTALL_RPATH}") +# If we are not building as part of LLVM (e.g., within the mono-repo), +# build Flang as a standalone project. This assumes the use of an +# existing LLVM install w/ external libraries. +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) -# Reminder: Setting CMAKE_CXX_COMPILER must be done before calling project() + project(Flang) -project(f18 CXX) + # FIXME: We currently rely on llvm-config to find all the + # details for us. This is deprecated. The cmake files installed + # with LLVM should be able to detect the LLVM install automatically. + # If not, you can use the LLMV_DIR to specify the path containing + # LLVMConfig.cmake. + set(CONFIG_OUTPUT) + if (LLVM_CONFIG) + set(LLVM_CONFIG_FOUND 1) + message(STATUS "Found LLVM_CONFIG as ${LLVM_CONFIG}...") + message(DEPRECATION "Using llvm-config is deprecated. Use the \ + LLVM installed CMake files instead. \ + This should allow CMake to automatically find \ + find your install. You can also use LLVM_DIR \ + to specify the path to LLVMConfig.cmake.") + set(CONFIG_COMMAND ${LLVM_CONFIG} + "--assertion-mode" + "--bindir" + "--libdir" + "--includedir" + "--prefix" + "--src-root" + "--cmakedir") + execute_process( + COMMAND ${CONFIG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE CONFIG_OUTPUT + ) -if( NOT CMAKE_BUILD_TYPE ) - set( CMAKE_BUILD_TYPE Debug ) -endif() + if (NOT HAD_ERROR) + string(REGEX REPLACE + "[ \t]*[\r\n]+[ \t]*" ";" + CONFIG_OUTPUT ${CONFIG_OUTPUT}) + else() + string(REPLACE ";" " " CONFIG_COMMAND_STR "${CONFIG_COMMAND}") + message(STATUS "${CONFIG_COMMAND_STR}") + message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}") + endif() -message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}" ) + list(GET CONFIG_OUTPUT 0 ENABLE_ASSERTIONS) + list(GET CONFIG_OUTPUT 1 TOOLS_BINARY_DIR) + list(GET CONFIG_OUTPUT 2 LIBRARY_DIR) + list(GET CONFIG_OUTPUT 3 INCLUDE_DIR) + list(GET CONFIG_OUTPUT 4 LLVM_OBJ_ROOT) + list(GET CONFIG_OUTPUT 5 MAIN_SRC_DIR) + list(GET CONFIG_OUTPUT 6 LLVM_CONFIG_CMAKE_PATH) -find_package(LLVM REQUIRED CONFIG) -message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION} in ${LLVM_DIR}") + # Normalize LLVM_CMAKE_PATH. --cmakedir might contain backslashes. + # CMake assumes slashes as PATH. + file(TO_CMAKE_PATH ${LLVM_CONFIG_CMAKE_PATH} LLVM_CMAKE_PATH) + endif() -# Get names for the LLVM libraries -# -# The full list of LLVM components can be obtained with -# -# llvm-config --components -# -# Similarly, the (static) libraries corresponding to some -# components (default is 'all') can be obtained with -# -# llvm-config --libs --link-static [component ...] -# -# See also -# http://llvm.org/docs/CMake.html#embedding-llvm-in-your-project -# https://stackoverflow.com/questions/41924375/llvm-how-to-specify-all-link-libraries-as-input-to-llvm-map-components-to-libna -# https://stackoverflow.com/questions/33948633/how-do-i-link-when-building-with-llvm-libraries + if (NOT MSVC_IDE) + set(LLVM_ENABLE_ASSERTIONS ${ENABLE_ASSERTIONS} + CACHE BOOL "Enable assertions") + # Assertions should follow llvm-config's. + mark_as_advanced(LLVM_ENABLE_ASSERTIONS) + endif() + + find_package(LLVM REQUIRED HINTS "${LLVM_CMAKE_PATH}") + list(APPEND CMAKE_MODULE_PATH ${LLVM_DIR}) + + # We can't check LLVM_CONFIG here, because find_package(LLVM ...) also sets + # LLVM_CONFIG. + if (NOT LLVM_CONFIG_FOUND) + # Pull values from LLVMConfig.cmake. We can drop this once the llvm-config + # path is removed. + set(TOOLS_BINARY_DIR ${LLVM_TOOLS_BINARY_DIR}) + set(LIBRARY_DIR ${LLVM_LIBRARY_DIR}) + set(INCLUDE_DIR ${LLVM_INCLUDE_DIR}) + set(LLVM_OBJ_DIR ${LLVM_BINARY_DIR}) + # The LLVM_CMAKE_PATH variable is set when doing non-standalone builds and + # used in this project, so we need to make sure we set this value. + # FIXME: LLVM_CMAKE_DIR comes from LLVMConfig.cmake. We should rename + # LLVM_CMAKE_PATH to LLVM_CMAKE_DIR throughout the project. + set(LLVM_CMAKE_PATH ${LLVM_CMAKE_DIR}) + endif() + + set(LLVM_TOOLS_BINARY_DIR ${TOOLS_BINARY_DIR} CACHE PATH "Path to llvm/bin") + set(LLVM_LIBRARY_DIR ${LIBRARY_DIR} CACHE PATH "Path to llvm/lib") + set(LLVM_MAIN_INCLUDE_DIR ${INCLUDE_DIR} CACHE PATH "Path to llvm/include") + set(LLVM_BINARY_DIR ${LLVM_OBJ_ROOT} CACHE PATH "Path to LLVM build tree") + set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree") + + # FIXME?? -- skipped tablegen stuff for now... + + option(LLVM_ENABLE_WARNINGS "Enable compiler warnings." ON) + option(LLVM_INSTALL_TOOLCHAIN_ONLY + "Only include toolchain files in the 'install' target." OFF) + + include(AddLLVM) + include(HandleLLVMOptions) + include(VersionFromVCS) + + set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}") + + if (NOT DEFINED LLVM_INCLUDE_TESTS) + set(LLVM_INCLUDE_TESTS ON) + endif() + + include_directories("${LLVM_BINARY_DIR}/include" "${LLVM_MAIN_INCLUDE_DIR}") + link_directories("${LLVM_LIBRARY_DIR}") + + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) + + if (LLVM_INCLUDE_TESTS) + set(Python_ADDITIONAL_VERSIONS 2.7) + include(FindPythonInterp) + if (NOT PYTHONINTERP_FOUND) + message(FATAL_ERROR + "Unable to find Python interpreter, required for builds and testing. + Please install Python or specify the PYTHON_EXECUTABLE CMake variable.") + endif() + + if (${PYTHON_VERSION_STRING} VERSION_LESS 2.7) + message(FATAL_ERROR "Python 2.7 or newer is required") + endif() + + + # Check prebuilt llvm/utils. + if (EXISTS ${LLVM_TOOLS_BINARY_DIR}/FileCheck${CMAKE_EXECUTABLE_SUFFIX} + AND EXISTS ${LLVM_TOOLS_BINARY_DIR}/count${CMAKE_EXECUTABLE_SUFFIX} + AND EXISTS ${LLVM_TOOLS_BINARY_DIR}/not${CMAKE_EXECUTABLE_SUFFIX}) + set(LLVM_UTILS_PROVIDED ON) + endif() + + if (EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) + # Note: path not really used, except for checking if lit was found + set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) + if (EXISTS ${LLVM_MAIN_SRC_DIR}/utils/llvm-lit) + add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/llvm-lit utils/llvm-lit) + endif() + if (NOT LLVM_UTILS_PROVIDED) + add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/FileCheck utils/FileCheck) + add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/count utils/count) + add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/not utils/not) + set(LLVM_UTILS_PROVIDED ON) + set(FLANG_TEST_DEPS FileCheck count not) + endif() + + set(UNITTEST_DIR ${LLVM_MAIN_SRC_DIR}/utils/unittest) + if (EXISTS ${UNITTEST_DIR}/googletest/include/gtest/gtest.h + AND NOT EXISTS ${LLVM_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX} + AND EXISTS ${UNITTEST_DIR}/CMakeLists.txt) + add_subdirectory(${UNITTEST_DIR} utils/unittest) + endif() + else() + # Seek installed Lit. + find_program(LLVM_LIT + NAMES llvm-lit lit.py lit + PATHS "${LLVM_MAIN_SRC_DIR}/utils/lit" + DOC "Path to lit.py") + endif() + + if (LLVM_LIT) + # Define the default arguments to use with 'lit', and an option for the user to override. + set(LIT_ARGS_DEFAULT "-sv") + if (MSVC OR XCODE) + set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar") + endif() + set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit") + + # On Win32 hosts, provide an option to specify the path to the GnuWin32 tools. + if (WIN32 AND NOT CYGWIN) + set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools") + endif() + else() + set(LLVM_INCLUDE_TESTS OFF) + endif() + endif() -include_directories(${LLVM_INCLUDE_DIRS}) -add_definitions(${LLVM_DEFINITIONS}) + set(FLANG_BUILD_STANDALONE 1) + set(BACKEND_PACKAGE_STRING "LLVM ${LLVM_PACKAGE_VERSION}") +else() + set(BACKEND_PACKAGE_STRING "${PACKAGE_STRING}") +endif() -if(LINK_WITH_FIR) +# Make sure that our source directory is on the current cmake module path so that +# we can include cmake files from this directory. +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") + +option(FLANG_LINK_WITH_FIR "Link Flang driver with FIR and LLVM" ON) +if (LINK_WITH_FIR) message(STATUS "Linking driver with FIR and LLVM") llvm_map_components_to_libnames(LLVM_COMMON_LIBS support) message(STATUS "LLVM libraries: ${LLVM_COMMON_LIBS}") endif() -if(CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - if(BUILD_WITH_CLANG_LIBRARIES) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdinc++ -I${BUILD_WITH_CLANG_LIBRARIES}/include/c++/v1 -DCLANG_LIBRARIES") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -Wl,-rpath,${BUILD_WITH_CLANG_LIBRARIES}/lib -L${BUILD_WITH_CLANG_LIBRARIES}/lib") - else() - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++") - endif() - if(GCC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --gcc-toolchain=${GCC}") - endif() - endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra -Werror") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wcast-qual") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wimplicit-fallthrough") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdelete-non-virtual-dtor") - set(CMAKE_CXX_FLAGS_RELEASE "-O2") - set(CMAKE_CXX_FLAGS_MINSIZEREL "-O2 '-DCHECK=(void)'") - set(CMAKE_CXX_FLAGS_DEBUG "-g -DDEBUGF18") +set(FLANG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(FLANG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) +set(MLIR_SOURCE_DIR ${LLVM_SOURCE_DIR}/projects/mlir) +set(MLIR_BINARY_DIR ${LLVM_BINARY_DIR}/projects/mlir) + +include_directories(BEFORE + ${FLANG_BINARY_DIR}/include + ${FLANG_SOURCE_DIR}/include + ${MLIR_BINARY_DIR}/include + ${MLIR_SOURCE_DIR}/include + ) + +set(MLIR_MAIN_SRC_DIR ${MLIR_SOURCE_DIR}) +set(MLIR_INCLUDE_DIR ${MLIR_SOURCE_DIR}/include) +set(MLIR_TABLEGEN_EXE mlir-tblgen) + +set(FLANG_C_INCLUDE_DIRS "" CACHE STRING + "Colon separated list of directories flang will search for headers.") + +set(GCC_INSTALL_PREFIX "" CACHE PATH "Directory where gcc is installed.") +set(DEFAULT_SYSROOT "" CACHE PATH + "Default to all compiler invocations for --sysroot=.") + +set(ENABLE_LINKER_BUILD_ID OFF CACHE BOOL "pass --build-id to ld") + +set(FLANG_DEFAULT_LINKER "" CACHE STRING + "Default linker to use (linker name or absolute path, empty for platform default)") + +set(FLANG_DEFAULT_RTLIB "" CACHE STRING "Default Fortran runtime library to use (\"libFortranRuntime\", empty for platform default)") +if (NOT(FLANG_DEFAULT_RTLIB STREQUAL "")) + message(WARNING "Resetting Flang's default runtime library to use platform default") + set(FLANG_DEFAULT_RTLIB "" CACHE STRING + "Default runtime library to use (\"libgcc\" or \"compiler-rt\", empty for platform default)" FORCE) +endif() + +# FIXME: Not handling OpenMP details here... + + +set(FLANG_VENDOR ${PACKAGE_VENDOR} CACHE STRING + "Vendor-specific text for showing with version information.") + + +if (FLANG_VENDOR) + add_definitions(-DFLANG_VENDOR="${FLANG_VENDOR} ") +endif() + +set(FLANG_REPOSITORY_STRING "" CACHE STRING + "Vendor-specific text for showing the repository the source is taken from.") + +if (FLANG_REPOSITORY_STRING) + add_definitions(-DFLANG_REPOSITORY_STRING="${FLANG_REPOSITORY_STRING}") +endif() + +set(FLANG_VENDOR_UTI "org.llvm.flang" CACHE STRING + "Vendor-specific uti.") + +# FIXME?? No Python bindings... + +# The libdir suffix must exactly match whatever LLVM's configuration used. +set(FLANG_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}") + +set(FLANG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(FLANG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR AND NOT MSVC_IDE) + message(FATAL_ERROR "In-source builds are not allowed. " +"Please create a directory and run cmake " +"from there, passing the path to this source directory as the last argument. " +"This process created the file `CMakeCache.txt' and the directory " +"`CMakeFiles'. Please delete them.") +endif() + +# Override version information to reflect early stages of flang vs. +# following LLVM's versioning info... +set(FLANG_VERSION_MAJOR "0") +set(FLANG_VERSION_MINOR "1") +set(FLANG_VERSION_PATCHLEVEL "0") + +# If FLANG_VERSION_* is specified, use it, if not use LLVM_VERSION_*. +if (NOT DEFINED FLANG_VERSION_MAJOR) + set(FLANG_VERSION_MAJOR ${LLVM_VERSION_MAJOR}) +endif() +if (NOT DEFINED FLANG_VERSION_MINOR) + set(FLANG_VERSION_MINOR ${LLVM_VERSION_MINOR}) +endif() +if (NOT DEFINED FLANG_VERSION_PATCHLEVEL) + set(FLANG_VERSION_PATCHLEVEL ${LLVM_VERSION_PATCH}) +endif() +# Unlike PACKAGE_VERSION, FLANG_VERSION does not include LLVM_VERSION_SUFFIX. +set(FLANG_VERSION "${FLANG_VERSION_MAJOR}.${FLANG_VERSION_MINOR}.${FLANG_VERSION_PATCHLEVEL}") +message(STATUS "Flang version: ${FLANG_VERSION}") + +# Configure the Version.inc file. +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/include/flang/Version.inc.in + ${CMAKE_CURRENT_BINARY_DIR}/include/flang/Version.inc) + +if (FLANG_LINK_WITH_FIR) + message(STATUS "Linking Flang driver with FIR and LLVM") + llvm_map_components_to_libnames(LLVM_COMMON_LIBS support) + message(STATUS "Flang using LLVM libraries: ${LLVM_COMMON_LIBS}") +endif() + +#set(FLANG_CXX_FLAGS "-std=c++17") + +# Add appropriate flags for GCC +if (LLVM_COMPILER_IS_GCC_COMPATIBLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -fno-rtti -fno-exceptions -Wall") + if (NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing -fno-semantic-interposition") + endif () + + # Enable -pedantic for Flang even if it's not enabled for LLVM. + if (NOT LLVM_ENABLE_PEDANTIC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic") + endif () + + if ((CMAKE_CXX_COMPILER_ID MATCHES "Clang")) + if (APPLE) + # don't add --gcc-toolchain + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --gcc-toolchain=${CMAKE_CXX_COMPILER}") + endif() + endif() + + check_cxx_compiler_flag("-Werror -Wnested-anon-types" CXX_SUPPORTS_NO_NESTED_ANON_TYPES_FLAG) + if (CXX_SUPPORTS_NO_NESTED_ANON_TYPES_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nested-anon-types") + endif() + + if (GCC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --gcc-toolchain=${GCC}") + endif () + + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUGF18") # Building shared libraries is death on performance with GCC by default # due to the need to preserve the right to override external entry points @@ -105,30 +357,157 @@ if(CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) if (BUILD_SHARED_LIBS AND NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-semantic-interposition") endif() + +endif () + +# Determine HOST_LINK_VERSION on Darwin. +set(HOST_LINK_VERSION) +if (APPLE) + set(LD_V_OUTPUT) + execute_process( + COMMAND sh -c "${CMAKE_LINKER} -v 2>&1 | head -1" + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LD_V_OUTPUT + ) + if (NOT HAD_ERROR) + if ("${LD_V_OUTPUT}" MATCHES ".*ld64-([0-9.]+).*") + string(REGEX REPLACE ".*ld64-([0-9.]+).*" "\\1" HOST_LINK_VERSION ${LD_V_OUTPUT}) + elseif ("${LD_V_OUTPUT}" MATCHES "[^0-9]*([0-9.]+).*") + string(REGEX REPLACE "[^0-9]*([0-9.]+).*" "\\1" HOST_LINK_VERSION ${LD_V_OUTPUT}) + endif() + else() + message(FATAL_ERROR "${CMAKE_LINKER} failed with status ${HAD_ERROR}") + endif() endif() -set(FLANG_VERSION_MAJOR "0") -set(FLANG_VERSION_MINOR "1") -set(FLANG_VERSION_PATCHLEVEL "0") -set(FLANG_VERSION "${FLANG_VERSION_MAJOR}.${FLANG_VERSION_MINOR}.${FLANG_VERSION_PATCHLEVEL}") -message(STATUS "FLANG version: ${FLANG_VERSION}") +include(CMakeParseArguments) +include(AddFlang) -set(FLANG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(FLANG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) +# set(CMAKE_INCLUDE_CURRENT_DIR ON) -include_directories(BEFORE - ${FLANG_BINARY_DIR}/include - ${FLANG_SOURCE_DIR}/include - ) +#include_directories(BEFORE +# ${CMAKE_CURRENT_BINARY_DIR}/include +# ${CMAKE_CURRENT_SOURCE_DIR}/include +# ) + +if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) + install(DIRECTORY include/flang + DESTINATION include + COMPONENT flang-headers + FILES_MATCHING + PATTERN "*.def" + PATTERN "*.h" + PATTERN "config.h" EXCLUDE + PATTERN ".svn" EXCLUDE + ) -enable_testing() + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/flang + DESTINATION include + COMPONENT flang-headers + FILES_MATCHING + PATTERN "CMakeFiles" EXCLUDE + PATTERN "*.inc" + PATTERN "*.h" + ) -add_subdirectory(include/flang) +# install(PROGRAMS utils/bash-autocomplete.sh +# DESTINATION share/flang +# ) + +endif() + +add_definitions(-D_GNU_SOURCE) + +option(FLANG_BUILD_TOOLS + "Build the Flang tools. If OFF, just generate build targets." ON) + +# Flang version information +set(FLANG_EXECUTABLE_VERSION + "${FLANG_VERSION_MAJOR}" CACHE STRING + "Major version number that will be appended to the flang executable name") + +set(LIBFLANG_LIBRARY_VERSION + "${FLANG_VERSION_MAJOR}" CACHE STRING + "Major version number that will be appended to the libflang library") + +mark_as_advanced(FLANG_EXECUTABLE_VERSION LIBFLANG_LIBRARY_VERSION) + +option(FLANG_INCLUDE_TESTS + "Generate build targets for the Flang unit tests." + ${LLVM_INCLUDE_TESTS}) + +add_subdirectory(include) add_subdirectory(lib) -add_subdirectory(runtime) -add_subdirectory(test) add_subdirectory(tools) +add_subdirectory(runtime) + +#option(FLANG_BUILD_EXAMPLES "Build Flang example programs by default." OFF) +#add_subdirectory(examples) + +# FIXME?? Do we need an order file like Clang (for Darwin)? + +#if (FLANG_INCLUDE_TESTS) +# if (EXISTS ${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include/gtest/gtest.h) +# add_subdirectory(unittests) +# list(APPEND FLANG_TEST_DEPS FlangUnitTests) +# list(APPEND FLANG_TEST_PARAMS +# flang_unit_site_config=${CMAKE_CURRENT_BINARY_DIR}/test/Unit/lit.site.cfg +# ) +# endif() + +# add_subdirectory(test) +# +# if (FLANG_BUILT_STANDALONE) +# # Add a global check rule now that all subdirectories have been traversed +# # and we know the total set of lit testsuites. +# get_property(LLVM_LIT_TESTSUITES GLOBAL PROPERTY LLVM_LIT_TESTSUITES) +# get_property(LLVM_LIT_PARAMS GLOBAL PROPERTY LLVM_LIT_PARAMS) +# get_property(LLVM_LIT_DEPENDS GLOBAL PROPERTY LLVM_LIT_DEPENDS) +# get_property(LLVM_LIT_EXTRA_ARGS GLOBAL PROPERTY LLVM_LIT_EXTRA_ARGS) +# get_property(LLVM_ADDITIONAL_TEST_TARGETS +# GLOBAL PROPERTY LLVM_ADDITIONAL_TEST_TARGETS) +# +# add_lit_target(check-all +# "Running all regression tests" +# ${LLVM_LIT_TESTSUITES} +# PARAMS ${LLVM_LIT_PARAMS} +# DEPENDS ${LLVM_LIT_DEPENDS} ${LLVM_ADDITIONAL_TEST_TARGETS} +# ARGS ${LLVM_LIT_EXTRA_ARGS} +# ) +# endif() +#endif() + +option(FLANG_INCLUDE_DOCS "Generate build targets for the Flang docs." + ${LLVM_INCLUDE_DOCS}) + +if (FLANG_INCLUDE_DOCS) + add_subdirectory(docs) +endif() + +# Custom target to install Flang libraries. +add_custom_target(flang-libraries) +set_target_properties(flang-libraries PROPERTIES FOLDER "Misc") + +if (NOT LLVM_ENABLE_IDE) + add_llvm_install_targets(install-flang-libraries + DEPENDS flang-libraries + COMPONENT flang-libraries) +endif() + +get_property(FLANG_LIBS GLOBAL PROPERTY FLANG_LIBS) +if (FLANG_LIBS) + list(REMOVE_DUPLICATES FLANG_LIBS) + foreach(lib ${FLANG_LIBS}) + add_dependencies(flang-libraries ${lib}) + if (NOT LLVM_ENABLE_IDE) + add_dependencies(install-flang-libraries install-${lib}) + endif() + endforeach() +endif() + +add_subdirectory(cmake/modules) configure_file( ${FLANG_SOURCE_DIR}/include/flang/Config/config.h.cmake ${FLANG_BINARY_DIR}/include/flang/Config/config.h) + diff --git a/cmake/modules/AddFlang.cmake b/cmake/modules/AddFlang.cmake new file mode 100644 index 000000000000..993b062aa758 --- /dev/null +++ b/cmake/modules/AddFlang.cmake @@ -0,0 +1,140 @@ +macro(set_flang_windows_version_resource_properties name) + if(DEFINED windows_resource_file) + set_windows_version_resource_properties(${name} ${windows_resource_file} + VERSION_MAJOR ${FLANG_VERSION_MAJOR} + VERSION_MINOR ${FLANG_VERSION_MINOR} + VERSION_PATCHLEVEL ${FLANG_VERSION_PATCHLEVEL} + VERSION_STRING "${FLANG_VERSION} (${BACKEND_PACKAGE_STRING})" + PRODUCT_NAME "flang") + endif() +endmacro() + +macro(add_flang_subdirectory name) + add_llvm_subdirectory(FLANG TOOL ${name}) +endmacro() + +macro(add_flang_library name) + cmake_parse_arguments(ARG + "SHARED" + "" + "ADDITIONAL_HEADERS" + ${ARGN}) + set(srcs) + if(MSVC_IDE OR XCODE) + # Add public headers + file(RELATIVE_PATH lib_path + ${FLANG_SOURCE_DIR}/lib/ + ${CMAKE_CURRENT_SOURCE_DIR} + ) + if(NOT lib_path MATCHES "^[.][.]") + file( GLOB_RECURSE headers + ${FLANG_SOURCE_DIR}/include/flang/${lib_path}/*.h + ${FLANG_SOURCE_DIR}/include/flang/${lib_path}/*.def + ) + set_source_files_properties(${headers} PROPERTIES HEADER_FILE_ONLY ON) + + if(headers) + set(srcs ${headers}) + endif() + endif() + endif(MSVC_IDE OR XCODE) + if(srcs OR ARG_ADDITIONAL_HEADERS) + set(srcs + ADDITIONAL_HEADERS + ${srcs} + ${ARG_ADDITIONAL_HEADERS} # It may contain unparsed unknown args. + ) + endif() + if(ARG_SHARED) + set(LIBTYPE SHARED) + else() + # llvm_add_library ignores BUILD_SHARED_LIBS if STATIC is explicitly set, + # so we need to handle it here. + if(BUILD_SHARED_LIBS) + set(LIBTYPE SHARED OBJECT) + else() + set(LIBTYPE STATIC OBJECT) + endif() + set_property(GLOBAL APPEND PROPERTY FLANG_STATIC_LIBS ${name}) + endif() + llvm_add_library(${name} ${LIBTYPE} ${ARG_UNPARSED_ARGUMENTS} ${srcs}) + + if(TARGET ${name}) + target_link_libraries(${name} INTERFACE ${LLVM_COMMON_LIBS}) + + if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY OR ${name} STREQUAL "libflang") + set(export_to_flangtargets) + if(${name} IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR + "flang-libraries" IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR + NOT LLVM_DISTRIBUTION_COMPONENTS) + set(export_to_flangtargets EXPORT FlangTargets) + set_property(GLOBAL PROPERTY FLANG_HAS_EXPORTS True) + endif() + + install(TARGETS ${name} + COMPONENT ${name} + ${export_to_flangtargets} + LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} + ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} + RUNTIME DESTINATION bin) + + if (NOT LLVM_ENABLE_IDE) + add_llvm_install_targets(install-${name} + DEPENDS ${name} + COMPONENT ${name}) + endif() + + set_property(GLOBAL APPEND PROPERTY FLANG_LIBS ${name}) + endif() + set_property(GLOBAL APPEND PROPERTY FLANG_EXPORTS ${name}) + else() + # Add empty "phony" target + add_custom_target(${name}) + endif() + + set_target_properties(${name} PROPERTIES FOLDER "Flang libraries") + set_flang_windows_version_resource_properties(${name}) +endmacro(add_flang_library) + +macro(add_flang_executable name) + add_llvm_executable( ${name} ${ARGN} ) + set_target_properties(${name} PROPERTIES FOLDER "Flang executables") + set_flang_windows_version_resource_properties(${name}) +endmacro(add_flang_executable) + +macro(add_flang_tool name) + if (NOT FLANG_BUILD_TOOLS) + set(EXCLUDE_FROM_ALL ON) + endif() + + add_flang_executable(${name} ${ARGN}) + add_dependencies(${name} flang-resource-headers) + + if (FLANG_BUILD_TOOLS) + set(export_to_flangtargets) + if(${name} IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR + NOT LLVM_DISTRIBUTION_COMPONENTS) + set(export_to_flangtargets EXPORT FlangTargets) + set_property(GLOBAL PROPERTY FLANG_HAS_EXPORTS True) + endif() + + install(TARGETS ${name} + ${export_to_flangtargets} + RUNTIME DESTINATION bin + COMPONENT ${name}) + + if(NOT LLVM_ENABLE_IDE) + add_llvm_install_targets(install-${name} + DEPENDS ${name} + COMPONENT ${name}) + endif() + set_property(GLOBAL APPEND PROPERTY FLANG_EXPORTS ${name}) + endif() +endmacro() + +macro(add_flang_symlink name dest) + add_llvm_tool_symlink(${name} ${dest} ALWAYS_GENERATE) + # Always generate install targets + llvm_install_symlink(${name} ${dest} ALWAYS_GENERATE) +endmacro() + diff --git a/cmake/modules/CMakeLists.txt b/cmake/modules/CMakeLists.txt new file mode 100644 index 000000000000..9d6288a38243 --- /dev/null +++ b/cmake/modules/CMakeLists.txt @@ -0,0 +1,73 @@ +# Generate a list of CMake library targets so that other CMake projects can +# link against them. LLVM calls its version of this file LLVMExports.cmake, but +# the usual CMake convention seems to be ${Project}Targets.cmake. +set(FLANG_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/flang) +set(flang_cmake_builddir "${CMAKE_BINARY_DIR}/${FLANG_INSTALL_PACKAGE_DIR}") + +# Keep this in sync with llvm/cmake/CMakeLists.txt! +set(LLVM_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm) +set(llvm_cmake_builddir "${LLVM_BINARY_DIR}/${LLVM_INSTALL_PACKAGE_DIR}") + +get_property(FLANG_EXPORTS GLOBAL PROPERTY FLANG_EXPORTS) +export(TARGETS ${FLANG_EXPORTS} FILE ${flang_cmake_builddir}/FlangTargets.cmake) + +# Generate FlangConfig.cmake for the build tree. +set(FLANG_CONFIG_CMAKE_DIR "${flang_cmake_builddir}") +set(FLANG_CONFIG_LLVM_CMAKE_DIR "${llvm_cmake_builddir}") +set(FLANG_CONFIG_EXPORTS_FILE "${flang_cmake_builddir}/FlangTargets.cmake") +set(FLANG_CONFIG_INCLUDE_DIRS + "${FLANG_SOURCE_DIR}/include" + "${FLANG_BINARY_DIR}/include" + ) +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/FlangConfig.cmake.in + ${flang_cmake_builddir}/FlangConfig.cmake + @ONLY) +set(FLANG_CONFIG_CMAKE_DIR) +set(FLANG_CONFIG_LLVM_CMAKE_DIR) +set(FLANG_CONFIG_EXPORTS_FILE) + +# Generate FlangConfig.cmake for the install tree. +set(FLANG_CONFIG_CODE " +# Compute the installation prefix from this LLVMConfig.cmake file location. +get_filename_component(FLANG_INSTALL_PREFIX \"\${CMAKE_CURRENT_LIST_FILE}\" PATH)") +# Construct the proper number of get_filename_component(... PATH) +# calls to compute the installation prefix. +string(REGEX REPLACE "/" ";" _count "${FLANG_INSTALL_PACKAGE_DIR}") +foreach(p ${_count}) + set(FLANG_CONFIG_CODE "${FLANG_CONFIG_CODE} +get_filename_component(FLANG_INSTALL_PREFIX \"\${FLANG_INSTALL_PREFIX}\" PATH)") +endforeach(p) +set(FLANG_CONFIG_CMAKE_DIR "\${FLANG_INSTALL_PREFIX}/${FLANG_INSTALL_PACKAGE_DIR}") +set(FLANG_CONFIG_LLVM_CMAKE_DIR "\${FLANG_INSTALL_PREFIX}/${LLVM_INSTALL_PACKAGE_DIR}") +set(FLANG_CONFIG_EXPORTS_FILE "\${FLANG_CMAKE_DIR}/FlangTargets.cmake") +set(FLANG_CONFIG_INCLUDE_DIRS + "\${FLANG_INSTALL_PREFIX}/include" + ) +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/FlangConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/FlangConfig.cmake + @ONLY) +set(FLANG_CONFIG_CODE) +set(FLANG_CONFIG_CMAKE_DIR) +set(FLANG_CONFIG_EXPORTS_FILE) + +if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) + get_property(flang_has_exports GLOBAL PROPERTY FLANG_HAS_EXPORTS) + if(flang_has_exports) + install(EXPORT FlangTargets DESTINATION ${FLANG_INSTALL_PACKAGE_DIR} + COMPONENT flang-cmake-exports) + endif() + + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/FlangConfig.cmake + DESTINATION ${FLANG_INSTALL_PACKAGE_DIR} + COMPONENT flang-cmake-exports) + + if(NOT LLVM_ENABLE_IDE) + # Add a dummy target so this can be used with LLVM_DISTRIBUTION_COMPONENTS + add_custom_target(flang-cmake-exports) + add_llvm_install_targets(install-flang-cmake-exports + COMPONENT flang-cmake-exports) + endif() +endif() diff --git a/cmake/modules/FlangConfig.cmake.in b/cmake/modules/FlangConfig.cmake.in new file mode 100644 index 000000000000..ba8c22294385 --- /dev/null +++ b/cmake/modules/FlangConfig.cmake.in @@ -0,0 +1,13 @@ +# This file allows users to call find_package(Flang) and pick up our targets. + +@FLANG_CONFIG_CODE@ + +find_package(LLVM REQUIRED CONFIG + HINTS "@FLANG_CONFIG_LLVM_CMAKE_DIR@") + +set(FLANG_EXPORTED_TARGETS "@FLANG_EXPORTS@") +set(FLANG_CMAKE_DIR "FCLANG_CONFIG_CMAKE_DIR@") +set(FLANG_INCLUDE_DIRS "@FLANG_CONFIG_INCLUDE_DIRS@") + +# Provide all our library targets to users. +include("@FLANG_CONFIG_EXPORTS_FILE@") diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt new file mode 100644 index 000000000000..d4915e101ea6 --- /dev/null +++ b/docs/CMakeLists.txt @@ -0,0 +1,92 @@ +if (DOXYGEN_FOUND) +if (LLVM_ENABLE_DOXYGEN) + set(abs_srcdir ${CMAKE_CURRENT_SOURCE_DIR}) + set(abs_builddir ${CMAKE_CURRENT_BINARY_DIR}) + + if (HAVE_DOT) + set(DOT ${LLVM_PATH_DOT}) + endif() + + if (LLVM_DOXYGEN_EXTERNAL_SEARCH) + set(enable_searchengine "YES") + set(searchengine_url "${LLVM_DOXYGEN_SEARCHENGINE_URL}") + set(enable_server_based_search "YES") + set(enable_external_search "YES") + set(extra_search_mappings "${LLVM_DOXYGEN_SEARCH_MAPPINGS}") + else() + set(enable_searchengine "NO") + set(searchengine_url "") + set(enable_server_based_search "NO") + set(enable_external_search "NO") + set(extra_search_mappings "") + endif() + + # If asked, configure doxygen for the creation of a Qt Compressed Help file. + if (LLVM_ENABLE_DOXYGEN_QT_HELP) + set(FLANG_DOXYGEN_QCH_FILENAME "org.llvm.flang.qch" CACHE STRING + "Filename of the Qt Compressed help file") + set(FLANG_DOXYGEN_QHP_NAMESPACE "org.llvm.flang" CACHE STRING + "Namespace under which the intermediate Qt Help Project file lives") + set(FLANG_DOXYGEN_QHP_CUST_FILTER_NAME "Flang ${FLANG_VERSION}" CACHE STRING + "See http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-filters") + set(FLANG_DOXYGEN_QHP_CUST_FILTER_ATTRS "Flang,${FLANG_VERSION}" CACHE STRING + "See http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes") + set(flang_doxygen_generate_qhp "YES") + set(flang_doxygen_qch_filename "${FLANG_DOXYGEN_QCH_FILENAME}") + set(flang_doxygen_qhp_namespace "${FLANG_DOXYGEN_QHP_NAMESPACE}") + set(flang_doxygen_qhelpgenerator_path "${LLVM_DOXYGEN_QHELPGENERATOR_PATH}") + set(flang_doxygen_qhp_cust_filter_name "${FLANG_DOXYGEN_QHP_CUST_FILTER_NAME}") + set(flang_doxygen_qhp_cust_filter_attrs "${FLANG_DOXYGEN_QHP_CUST_FILTER_ATTRS}") + else() + set(flang_doxygen_generate_qhp "NO") + set(flang_doxygen_qch_filename "") + set(flang_doxygen_qhp_namespace "") + set(flang_doxygen_qhelpgenerator_path "") + set(flang_doxygen_qhp_cust_filter_name "") + set(flang_doxygen_qhp_cust_filter_attrs "") + endif() + + option(LLVM_DOXYGEN_SVG + "Use svg instead of png files for doxygen graphs." OFF) + if (LLVM_DOXYGEN_SVG) + set(DOT_IMAGE_FORMAT "svg") + else() + set(DOT_IMAGE_FORMAT "png") + endif() + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doxygen.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg @ONLY) + + set(abs_top_srcdir) + set(abs_top_builddir) + set(DOT) + set(enable_searchengine) + set(searchengine_url) + set(enable_server_based_search) + set(enable_external_search) + set(extra_search_mappings) + set(flang_doxygen_generate_qhp) + set(flang_doxygen_qch_filename) + set(flang_doxygen_qhp_namespace) + set(flang_doxygen_qhelpgenerator_path) + set(flang_doxygen_qhp_cust_filter_name) + set(flang_doxygen_qhp_cust_filter_attrs) + set(DOT_IMAGE_FORMAT) + + add_custom_target(doxygen-flang + COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating flang doxygen documentation." VERBATIM) + + if (LLVM_BUILD_DOCS) + add_dependencies(doxygen doxygen-flang) + endif() + + if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doxygen/html + DESTINATION docs/html) + endif() +endif() +endif() + +# FIXME: skipping sphinx for now... diff --git a/docs/doxygen.cfg.in b/docs/doxygen.cfg.in new file mode 100644 index 000000000000..cf49203c0f91 --- /dev/null +++ b/docs/doxygen.cfg.in @@ -0,0 +1,2293 @@ +# Doxyfile 1.8.6 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = flang + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @PACKAGE_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = @abs_builddir@/doxygen + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = @abs_srcdir@/.. + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = @abs_srcdir@/../include + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 3 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = YES + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = NO + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = NO + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = @abs_srcdir@/../include \ + @abs_srcdir@/../lib \ + @abs_srcdir@/doxygen-mainpage.dox + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = @abs_srcdir@/../examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = @abs_srcdir@/img + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 4 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = flang:: + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = @flang_doxygen_generate_qhp@ + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = @flang_doxygen_qch_filename@ + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = @flang_doxygen_qhp_namespace@ + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = @flang_doxygen_qhp_cust_filter_name@ + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = @flang_doxygen_qhp_cust_filter_attrs@ + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = @flang_doxygen_qhelpgenerator_path@ + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , / expression + M::Type exprType(const SomeExpr *expr) { + return translateSomeExprToFIRType(&mlirContext, expr); + } + M::Type refExprType(const SomeExpr *expr) { + auto type{translateSomeExprToFIRType(&mlirContext, expr)}; + return fir::ReferenceType::get(type); + } + + int getDefaultIntegerKind() { + return getDefaultKinds().GetDefaultKind(Co::TypeCategory::Integer); + } + M::Type getDefaultIntegerType() { + return M::IntegerType::get(8 * getDefaultIntegerKind(), &mlirContext); + } + int getDefaultLogicalKind() { + return getDefaultKinds().GetDefaultKind(Co::TypeCategory::Logical); + } + M::Type getDefaultLogicalType() { + return fir::LogicalType::get(&mlirContext, getDefaultLogicalKind()); + } + + M::Value *createFIRAddr(M::Location loc, const SomeExpr *expr) { + return createSomeAddress(loc, build(), *expr, symbolMap); + } + M::Value *createFIRExpr(M::Location loc, const SomeExpr *expr) { + return createSomeExpression(loc, build(), *expr, symbolMap); + } + M::Value *createTemp(M::Type type, Se::Symbol *symbol = nullptr) { + return createTemporary(toLocation(), build(), symbolMap, type, symbol); + } + + M::FuncOp genFunctionMLIR(llvm::StringRef callee, M::FunctionType funcTy) { + if (auto func{getNamedFunction(callee)}) { + return func; + } + return createFunction(getMod(), callee, funcTy); + } + + M::FuncOp genRuntimeFunction(RuntimeEntryCode rec, int kind) { + return genFunctionMLIR( + getRuntimeEntryName(rec), getRuntimeEntryType(rec, mlirContext, kind)); + } + + template DoBoundsInfo *getBoundsInfo(const T &linearOp) { + auto &st{std::get>(linearOp.v->t)}; + lastKnownPos_ = st.source; + auto *s{&st.statement}; + auto iter{doMap.find(s)}; + if (iter != doMap.end()) { + return &iter->second; + } + assert(false && "DO context not present"); + return nullptr; + } + + // Simple scalar expression builders + // TODO: handle REAL and COMPLEX (iff needed) + template + M::Value *genCompare(M::Value *lhs, M::Value *rhs) { + auto lty{lhs->getType()}; + assert(lty == rhs->getType()); + if (lty.isIntOrIndex()) { + return build().create(lhs->getLoc(), ICMPOPC, lhs, rhs); + } + if (fir::LogicalType::kindof(lty.getKind())) { + return build().create(lhs->getLoc(), ICMPOPC, lhs, rhs); + } + if (fir::CharacterType::kindof(lty.getKind())) { + // return build().create(lhs->getLoc(), ); + return {}; + } + assert(false && "cannot generate operation on this type"); + return {}; + } + M::Value *genGE(M::Value *lhs, M::Value *rhs) { + return genCompare(lhs, rhs); + } + M::Value *genLE(M::Value *lhs, M::Value *rhs) { + return genCompare(lhs, rhs); + } + M::Value *genEQ(M::Value *lhs, M::Value *rhs) { + return genCompare(lhs, rhs); + } + M::Value *genAND(M::Value *lhs, M::Value *rhs) { + return build().create(lhs->getLoc(), lhs, rhs); + } + + void genMLIR(AnalysisData &ad, std::list &operations); + + // Control flow destination + void genMLIR(bool lastWasLabel, const Fl::LabelOp &op) { + if (lastWasLabel) { + blkMap().insert({op.get(), build().getInsertionBlock()}); + } else { + auto *currBlock{build().getInsertionBlock()}; + auto *newBlock{createBlock(&build())}; + blkMap().insert({op.get(), newBlock}); + if (!noInsPt) { + build().setInsertionPointToEnd(currBlock); + build().create(toLocation(), newBlock); + } + build().setInsertionPointToStart(newBlock); + } + } + + // Goto statements + void genMLIR(const Fl::GotoOp &op) { + auto iter{blkMap().find(op.target)}; + if (iter != blkMap().end()) { + build().create(toLocation(), iter->second); + } else { + using namespace std::placeholders; + edgeQ.emplace_back(std::bind( + [](M::OpBuilder *builder, M::Block *block, Fl::LabelRef dest, + M::Location location, const LabelMapType &map) { + builder->setInsertionPointToEnd(block); + assert(map.find(dest) != map.end() && "no destination"); + builder->create(location, map.find(dest)->second); + }, + &build(), build().getInsertionBlock(), op.target, toLocation(), _1)); + } + noInsPt = true; + } + void genMLIR(const Fl::ReturnOp &op) { + std::visit([&](const auto *stmt) { genMLIR(*stmt); }, op.u); + noInsPt = true; + } + void genMLIR(const Fl::ConditionalGotoOp &op) { + std::visit( + [&](const auto *stmt) { genMLIR(*stmt, op.trueLabel, op.falseLabel); }, + op.u); + noInsPt = true; + } + + void genMLIR(const Fl::SwitchIOOp &op); + + // CALL with alt-return value returned + void genMLIR(const Fl::SwitchOp &op, const Pa::CallStmt &stmt) { + auto loc{toLocation(op.source)}; + // FIXME + (void)loc; + } + void genMLIR(const Fl::SwitchOp &op, const Pa::ComputedGotoStmt &stmt) { + auto loc{toLocation(op.source)}; + auto *exp{Se::GetExpr(std::get(stmt.t))}; + auto *e1{createFIRExpr(loc, exp)}; + // FIXME + (void)e1; + } + void genMLIR(const Fl::SwitchOp &op, const Pa::ArithmeticIfStmt &stmt) { + auto loc{toLocation(op.source)}; + auto *exp{Se::GetExpr(std::get(stmt.t))}; + auto *e1{createFIRExpr(loc, exp)}; + // FIXME + (void)e1; + } + M::Value *fromCaseValue(const M::Location &locs, const Pa::CaseValue &val) { + return createFIRExpr(locs, Se::GetExpr(val)); + } + void genMLIR(const Fl::SwitchOp &op, const Pa::CaseConstruct &stmt); + void genMLIR(const Fl::SwitchOp &op, const Pa::SelectRankConstruct &stmt); + void genMLIR(const Fl::SwitchOp &op, const Pa::SelectTypeConstruct &stmt); + void genMLIR(const Fl::SwitchOp &op) { + std::visit([&](auto *construct) { genMLIR(op, *construct); }, op.u); + noInsPt = true; + } + + void genMLIR(AnalysisData &ad, const Fl::ActionOp &op); + + void pushDoContext(const Pa::NonLabelDoStmt *doStmt, + M::Value *doVar = nullptr, M::Value *counter = nullptr, + M::Value *stepExpr = nullptr) { + doMap.emplace(doStmt, DoBoundsInfo{doVar, counter, stepExpr}); + } + + void genLoopEnterMLIR(const Pa::LoopControl::Bounds &bounds, + const Pa::NonLabelDoStmt *stmt, const Pa::CharBlock &source) { + auto loc{toLocation(source)}; + auto *nameExpr{bounds.name.thing.symbol}; + auto *name{createTemp(getDefaultIntegerType(), nameExpr)}; + // evaluate e1, e2 [, e3] ... + auto *lowerExpr{Se::GetExpr(bounds.lower)}; + auto *e1{createFIRExpr(loc, lowerExpr)}; + auto *upperExpr{Se::GetExpr(bounds.upper)}; + auto *e2{createFIRExpr(loc, upperExpr)}; + M::Value *e3; + if (bounds.step.has_value()) { + auto *stepExpr{Se::GetExpr(bounds.step)}; + e3 = createFIRExpr(loc, stepExpr); + } else { + auto attr{build().getIntegerAttr(e2->getType(), 1)}; + e3 = build().create(loc, attr); + } + // name <- e1 + build().create(loc, e1, name); + auto tripCounter{createTemp(getDefaultIntegerType())}; + // See 11.1.7.4.1, para. 1, item (3) + // totalTrips ::= iteration count = a + // where a = (e2 - e1 + e3) / e3 if a > 0 and 0 otherwise + auto c1{build().create(loc, e2, e1)}; + auto c2{build().create(loc, c1.getResult(), e3)}; + auto c3{build().create(loc, c2.getResult(), e3)}; + auto *totalTrips{c3.getResult()}; + build().create(loc, totalTrips, tripCounter); + pushDoContext(stmt, name, tripCounter, e3); + } + + void genLoopEnterMLIR(const Pa::ScalarLogicalExpr &logicalExpr, + const Pa::NonLabelDoStmt *stmt, const Pa::CharBlock &source) { + // See 11.1.7.4.1, para. 2 + // See BuildLoopLatchExpression() + pushDoContext(stmt); + } + void genLoopEnterMLIR(const Pa::LoopControl::Concurrent &concurrent, + const Pa::NonLabelDoStmt *stmt, const Pa::CharBlock &source) { + // See 11.1.7.4.2 + // FIXME + } + void genEnterMLIR(const Pa::DoConstruct &construct) { + auto &stmt{std::get>(construct.t)}; + lastKnownPos_ = stmt.source; + const Pa::NonLabelDoStmt &ss{stmt.statement}; + auto &ctrl{std::get>(ss.t)}; + if (ctrl.has_value()) { + std::visit([&](const auto &x) { genLoopEnterMLIR(x, &ss, stmt.source); }, + ctrl->u); + } else { + // loop forever (See 11.1.7.4.1, para. 2) + pushDoContext(&ss); + } + } + template void genEnterMLIR(const A &construct) { + // FIXME: add other genEnterMLIR() members + } + void genMLIR(const Fl::BeginOp &op) { + std::visit([&](auto *construct) { genEnterMLIR(*construct); }, op.u); + } + + void genExitMLIR(const Pa::DoConstruct &construct) { + auto &stmt{std::get>(construct.t)}; + lastKnownPos_ = stmt.source; + const Pa::NonLabelDoStmt &ss{stmt.statement}; + auto &ctrl{std::get>(ss.t)}; + if (ctrl.has_value() && + std::holds_alternative(ctrl->u)) { + doMap.erase(&ss); + } + noInsPt = true; // backedge already processed + } + void genMLIR(const Fl::EndOp &op) { + if (auto *construct{std::get_if(&op.u)}) + genExitMLIR(**construct); + } + + void genMLIR(AnalysisData &ad, const Fl::IndirectGotoOp &op); + void genMLIR(const Fl::DoIncrementOp &op) { + auto *info{getBoundsInfo(op)}; + if (info->doVar && info->stepExpr) { + // add: do_var = do_var + e3 + auto load{ + build().create(info->doVar->getLoc(), info->doVar)}; + auto incremented{build().create( + load.getLoc(), load.getResult(), info->stepExpr)}; + build().create(load.getLoc(), incremented, info->doVar); + // add: counter-- + auto loadCtr{build().create( + info->counter->getLoc(), info->counter)}; + auto one{build().create( + loadCtr.getLoc(), build().getIntegerAttr(loadCtr.getType(), 1))}; + auto decremented{build().create( + loadCtr.getLoc(), loadCtr.getResult(), one)}; + build().create( + loadCtr.getLoc(), decremented, info->counter); + } + } + void genMLIR(const Fl::DoCompareOp &op) { + auto *info{getBoundsInfo(op)}; + if (info->doVar && info->stepExpr) { + // add: cond = counter > 0 (signed) + auto load{build().create( + info->counter->getLoc(), info->counter)}; + auto zero{build().create( + load.getLoc(), build().getIntegerAttr(load.getType(), 0))}; + auto cond{build().create( + load.getLoc(), M::CmpIPredicate::SGT, load, zero)}; + info->condition = cond; + } + } + void genMLIR(const Pa::FailImageStmt &stmt) { + auto callee{genRuntimeFunction(FIRT_FAIL_IMAGE, 0)}; + llvm::SmallVector operands; // FAIL IMAGE has no args + build().create(toLocation(), callee, operands); + build().create(toLocation()); + } + void genMLIR(const Pa::ReturnStmt &stmt) { + build().create(toLocation()); // FIXME: argument(s)? + } + void genMLIR(const Pa::StopStmt &stmt) { + auto callee{genRuntimeFunction( + isStopStmt(std::get(stmt.t)) ? FIRT_STOP + : FIRT_ERROR_STOP, + getDefaultIntegerKind())}; + // 2 args: stop-code-opt, quiet-opt + llvm::SmallVector operands; + build().create(toLocation(), callee, operands); + build().create(toLocation()); + } + + // Conditional branch-like statements + template + void genMLIR( + const A &tuple, Fl::LabelRef trueLabel, Fl::LabelRef falseLabel) { + auto *exprRef{Se::GetExpr(std::get(tuple))}; + assert(exprRef && "condition expression missing"); + auto *cond{createFIRExpr(toLocation(), exprRef)}; + genCondBranch(cond, trueLabel, falseLabel); + } + void genMLIR(const Pa::Statement &stmt, + Fl::LabelRef trueLabel, Fl::LabelRef falseLabel) { + lastKnownPos_ = stmt.source; + genMLIR(stmt.statement.t, trueLabel, falseLabel); + } + void genMLIR(const Pa::Statement &stmt, + Fl::LabelRef trueLabel, Fl::LabelRef falseLabel) { + lastKnownPos_ = stmt.source; + genMLIR(stmt.statement.t, trueLabel, falseLabel); + } + void genMLIR( + const Pa::IfStmt &stmt, Fl::LabelRef trueLabel, Fl::LabelRef falseLabel) { + genMLIR(stmt.t, trueLabel, falseLabel); + } + + M::Value *getTrueConstant() { + auto attr{build().getBoolAttr(true)}; + return build().create(toLocation(), attr).getResult(); + } + + // Conditional branch to enter loop body or exit + void genMLIR(const Pa::Statement &stmt, + Fl::LabelRef trueLabel, Fl::LabelRef falseLabel) { + lastKnownPos_ = stmt.source; + auto &loopCtrl{std::get>(stmt.statement.t)}; + M::Value *condition{nullptr}; + if (loopCtrl.has_value()) { + std::visit(Co::visitors{ + [&](const parser::LoopControl::Bounds &) { + auto iter{doMap.find(&stmt.statement)}; + assert(iter != doMap.end()); + condition = iter->second.condition->getResult(0); + }, + [&](const parser::ScalarLogicalExpr &logical) { + auto loc{toLocation(stmt.source)}; + auto *exp{Se::GetExpr(logical)}; + condition = createFIRExpr(loc, exp); + }, + [&](const parser::LoopControl::Concurrent &concurrent) { + // FIXME: incorrectly lowering DO CONCURRENT + condition = getTrueConstant(); + }, + }, + loopCtrl->u); + } else { + condition = getTrueConstant(); + } + assert(condition && "condition must be a Value"); + genCondBranch(condition, trueLabel, falseLabel); + } + + // Action statements + void genMLIR(const Pa::AllocateStmt &stmt); + void genMLIR(const Pa::AssignmentStmt &stmt) { + auto *rhs{Se::GetExpr(std::get(stmt.t))}; + auto *lhs{Se::GetExpr(std::get(stmt.t))}; + auto loc{toLocation()}; + build().create( + loc, createFIRExpr(loc, rhs), createFIRAddr(loc, lhs)); + } + void genMLIR(const Pa::BackspaceStmt &stmt); + void genMLIR(const Pa::CallStmt &stmt); + void genMLIR(const Pa::CloseStmt &stmt); + void genMLIR(const Pa::DeallocateStmt &stmt); + void genMLIR(const Pa::EndfileStmt &stmt); + void genMLIR(const Pa::EventPostStmt &stmt); + void genMLIR(const Pa::EventWaitStmt &stmt); + void genMLIR(const Pa::FlushStmt &stmt); + void genMLIR(const Pa::FormTeamStmt &stmt); + void genMLIR(const Pa::InquireStmt &stmt); + void genMLIR(const Pa::LockStmt &stmt); + void genMLIR(const Pa::NullifyStmt &stmt); + void genMLIR(const Pa::OpenStmt &stmt); + void genMLIR(const Pa::PointerAssignmentStmt &stmt); + void genMLIR(const Pa::PrintStmt &stmt); + void genMLIR(const Pa::ReadStmt &stmt); + void genMLIR(const Pa::RewindStmt &stmt); + void genMLIR(const Pa::SyncAllStmt &stmt); + void genMLIR(const Pa::SyncImagesStmt &stmt); + void genMLIR(const Pa::SyncMemoryStmt &stmt); + void genMLIR(const Pa::SyncTeamStmt &stmt); + void genMLIR(const Pa::UnlockStmt &stmt); + void genMLIR(const Pa::WaitStmt &stmt); + void genMLIR(const Pa::WhereStmt &stmt); + void genMLIR(const Pa::WriteStmt &stmt); + void genMLIR(const Pa::ForallStmt &stmt); + void genMLIR(AnalysisData &ad, const Pa::AssignStmt &stmt); + void genMLIR(const Pa::PauseStmt &stmt); + + template + void translateRoutine( + const A &routine, const std::string &name, const Se::Symbol *funcSym); + + void genCondBranch( + M::Value *cond, Fl::LabelRef trueBlock, Fl::LabelRef falseBlock) { + auto trueIter{blkMap().find(trueBlock)}; + auto falseIter{blkMap().find(falseBlock)}; + if (trueIter != blkMap().end() && falseIter != blkMap().end()) { + llvm::SmallVector blanks; + build().create(toLocation(), cond, trueIter->second, + blanks, falseIter->second, blanks); + } else { + using namespace std::placeholders; + edgeQ.emplace_back(std::bind( + [](M::OpBuilder *builder, M::Block *block, M::Value *cnd, + Fl::LabelRef trueDest, Fl::LabelRef falseDest, + M::Location location, const LabelMapType &map) { + llvm::SmallVector blk; + builder->setInsertionPointToEnd(block); + auto tdp{map.find(trueDest)}; + auto fdp{map.find(falseDest)}; + assert(tdp != map.end() && fdp != map.end()); + builder->create( + location, cnd, tdp->second, blk, fdp->second, blk); + }, + &build(), build().getInsertionBlock(), cond, trueBlock, falseBlock, + toLocation(), _1)); + } + } + + template + void genSwitchBranch(const M::Location &loc, M::Value *selector, + std::list &&conditions, + const std::vector &labels) { + assert(conditions.size() == labels.size()); + bool haveAllLabels{true}; + std::size_t u{0}; + // do we already have all the targets? + for (auto last{labels.size()}; u != last; ++u) { + haveAllLabels = blkMap().find(labels[u]) != blkMap().end(); + if (!haveAllLabels) break; + } + if (haveAllLabels) { + // yes, so generate the FIR operation now + u = 0; + std::vector conds; + std::vector blocks; + std::vector> blockArgs; + llvm::SmallVector blanks; + for (auto cond : conditions) { + conds.emplace_back(cond); + blocks.emplace_back(blkMap().find(labels[u++])->second); + blockArgs.emplace_back(blanks); + } + build().create(loc, selector, conds, blocks, blockArgs); + } else { + // no, so queue the FIR operation for later + using namespace std::placeholders; + edgeQ.emplace_back(std::bind( + [](M::OpBuilder *builder, M::Block *block, M::Value *sel, + const std::list &conditions, + const std::vector &labels, M::Location location, + const LabelMapType &map) { + std::size_t u{0}; + std::vector conds; + std::vector blocks; + std::vector> blockArgs; + llvm::SmallVector blanks; + for (auto &cond : conditions) { + auto iter{map.find(labels[u++])}; + assert(iter != map.end()); + conds.emplace_back(cond); + blocks.emplace_back(iter->second); + blockArgs.emplace_back(blanks); + } + builder->setInsertionPointToEnd(block); + builder->create(location, sel, conds, blocks, blockArgs); + }, + &build(), build().getInsertionBlock(), selector, conditions, labels, + loc, _1)); + } + } + + void finalizeQueued() { + for (auto &edgeFunc : edgeQ) { + edgeFunc(blkMap()); + } + } + +public: + MLIRConverter(BurnsideBridge &bridge) + : mlirContext{bridge.getMLIRContext()}, module_{bridge.getModule()} {} + MLIRConverter() = delete; + + M::ModuleOp getModule() { return getMod(); } + + template constexpr bool Pre(const A &) { return true; } + template constexpr void Post(const A &) {} + + /// Translate the various routines from the parse tree + void Post(const Pa::MainProgram &mainp) { + std::string mainName{"_MAIN"s}; + if (auto &ps{ + std::get>>(mainp.t)}) { + mainName = ps->statement.v.ToString(); + lastKnownPos_ = ps->source; + } + translateRoutine(mainp, mainName, nullptr); + } + void Post(const Pa::FunctionSubprogram &subp) { + auto &stmt{std::get>(subp.t)}; + lastKnownPos_ = stmt.source; + auto &name{std::get(stmt.statement.t)}; + translateRoutine(subp, name.ToString(), name.symbol); + } + void Post(const Pa::SubroutineSubprogram &subp) { + auto &stmt{std::get>(subp.t)}; + lastKnownPos_ = stmt.source; + auto &name{std::get(stmt.statement.t)}; + translateRoutine(subp, name.ToString(), name.symbol); + } +}; + +/// SELECT CASE +/// Build a switch-like structure for a SELECT CASE +void MLIRConverter::genMLIR( + const Fl::SwitchOp &op, const Pa::CaseConstruct &stmt) { + auto loc{toLocation(op.source)}; + auto &cstm{std::get>(stmt.t)}; + auto *exp{Se::GetExpr(std::get>(cstm.statement.t))}; + auto *e1{createFIRExpr(loc, exp)}; + auto &cases{std::get>(stmt.t)}; + std::list conds; + // Per C1145, we know each `case-expr` must have type INTEGER, CHARACTER, or + // LOGICAL + for (auto &sel : cases) { + auto &cs{std::get>(sel.t)}; + auto locs{toLocation(cs.source)}; + auto &csel{std::get(cs.statement.t)}; + std::visit( + Co::visitors{ + [&](const std::list &ranges) { + for (auto &r : ranges) { + std::visit(Co::visitors{ + [&](const Pa::CaseValue &val) { + auto *term{fromCaseValue(locs, val)}; + conds.emplace_back(genEQ(e1, term)); + }, + [&](const Pa::CaseValueRange::Range &rng) { + fir::SelectCaseOp::Conditions rangeComparison = + nullptr; + if (rng.lower.has_value()) { + auto *term{fromCaseValue(locs, *rng.lower)}; + // rc = e1 >= lower.term + rangeComparison = genGE(e1, term); + } + if (rng.upper.has_value()) { + auto *term{fromCaseValue(locs, *rng.upper)}; + // c = e1 <= upper.term + auto *comparison{genLE(e1, term)}; + // rc = if rc then (rc && c) else c + if (rangeComparison) { + rangeComparison = + genAND(rangeComparison, comparison); + } else { + rangeComparison = comparison; + } + } + conds.emplace_back(rangeComparison); + }, + }, + r.u); + } + }, + [&](const Pa::Default &) { conds.emplace_back(getTrueConstant()); }, + }, + csel.u); + } + genSwitchBranch(loc, e1, std::move(conds), op.refs); +} + +/// SELECT RANK +/// Build a switch-like structure for a SELECT RANK +void MLIRConverter::genMLIR( + const Fl::SwitchOp &op, const Pa::SelectRankConstruct &stmt) { + auto loc{toLocation(op.source)}; + auto &rstm{std::get>(stmt.t)}; + auto *exp{std::visit([](auto &x) { return Se::GetExpr(x); }, + std::get(rstm.statement.t).u)}; + auto *e1{createFIRExpr(loc, exp)}; + auto &ranks{std::get>(stmt.t)}; + std::list conds; + for (auto &r : ranks) { + auto &rs{std::get>(r.t)}; + auto &rank{std::get(rs.statement.t)}; + std::visit( + Co::visitors{ + [&](const Pa::ScalarIntConstantExpr &ex) { + auto *ie{createFIRExpr(loc, Se::GetExpr(ex))}; + conds.emplace_back(ie); + }, + [&](const Pa::Star &) { + // FIXME: using a bogon for now. Special value per + // whatever the runtime returns. + auto attr{build().getIntegerAttr(e1->getType(), -1)}; + conds.emplace_back(build().create(loc, attr)); + }, + [&](const Pa::Default &) { conds.emplace_back(getTrueConstant()); }, + }, + rank.u); + } + // FIXME: fix the type of the function + auto callee{genRuntimeFunction(FIRT_GET_RANK, 0)}; + llvm::SmallVector operands{e1}; + auto e3{build().create(loc, callee, operands)}; + genSwitchBranch( + loc, e3.getResult(0), std::move(conds), op.refs); +} + +/// SELECT TYPE +/// Build a switch-like structure for a SELECT TYPE +void MLIRConverter::genMLIR( + const Fl::SwitchOp &op, const Pa::SelectTypeConstruct &stmt) { + auto loc{toLocation(op.source)}; + auto &tstm{std::get>(stmt.t)}; + auto *exp{std::visit([](auto &x) { return Se::GetExpr(x); }, + std::get(tstm.statement.t).u)}; + auto *e1{createFIRExpr(loc, exp)}; + auto &types{std::get>(stmt.t)}; + std::list conds; + for (auto &t : types) { + auto &ts{std::get>(t.t)}; + auto &ty{std::get(ts.statement.t)}; + std::visit( + Co::visitors{ + [&](const Pa::TypeSpec &) { + // FIXME: add arguments + auto func{genRuntimeFunction(FIRT_ISA_TYPE, 0)}; + llvm::SmallVector operands; + auto call{build().create(loc, func, operands)}; + conds.emplace_back(call.getResult(0)); + }, + [&](const Pa::DerivedTypeSpec &) { + // FIXME: add arguments + auto func{genRuntimeFunction(FIRT_ISA_SUBTYPE, 0)}; + llvm::SmallVector operands; + auto call{build().create(loc, func, operands)}; + conds.emplace_back(call.getResult(0)); + }, + [&](const Pa::Default &) { conds.emplace_back(getTrueConstant()); }, + }, + ty.u); + } + auto callee{genRuntimeFunction(FIRT_GET_ELETYPE, 0)}; + llvm::SmallVector operands{e1}; + auto e3{build().create(loc, callee, operands)}; + genSwitchBranch( + loc, e3.getResult(0), std::move(conds), op.refs); +} + +void MLIRConverter::genMLIR(const Fl::SwitchIOOp &op) {} + +void MLIRConverter::genMLIR(const Pa::AllocateStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::BackspaceStmt &stmt) { + // builder->create(stmt.v); +} +void MLIRConverter::genMLIR(const Pa::CallStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::CloseStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::DeallocateStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::EndfileStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::EventPostStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::EventWaitStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::FlushStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::FormTeamStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::InquireStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::LockStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::NullifyStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::OpenStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::PointerAssignmentStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::PrintStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::ReadStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::RewindStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::SyncAllStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::SyncImagesStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::SyncMemoryStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::SyncTeamStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::UnlockStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::WaitStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::WhereStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::WriteStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::ForallStmt &stmt) {} +void MLIRConverter::genMLIR(AnalysisData &ad, const Pa::AssignStmt &stmt) {} +void MLIRConverter::genMLIR(const Pa::PauseStmt &stmt) {} + +/// translate action statements +void MLIRConverter::genMLIR(AnalysisData &ad, const Fl::ActionOp &op) { + lastKnownPos_ = op.v->source; + std::visit( + Co::visitors{ + [](const Pa::ContinueStmt &) { assert(false); }, + [](const Pa::FailImageStmt &) { assert(false); }, + [](const Co::Indirection &) { assert(false); }, + [](const Co::Indirection &) { assert(false); }, + [](const Co::Indirection &) { assert(false); }, + [](const Co::Indirection &) { assert(false); }, + [](const Co::Indirection &) { assert(false); }, + [](const Co::Indirection &) { assert(false); }, + [](const Co::Indirection &) { assert(false); }, + [](const Co::Indirection &) { assert(false); }, + [](const Co::Indirection &) { assert(false); }, + [&](const Co::Indirection &assign) { + genMLIR(ad, assign.value()); + }, + [&](const auto &stmt) { genMLIR(stmt.value()); }, + }, + op.v->statement.u); +} + +void MLIRConverter::genMLIR(AnalysisData &ad, const Fl::IndirectGotoOp &op) { + // add or queue an igoto +} + +void MLIRConverter::genMLIR(AnalysisData &ad, std::list &operations) { + bool lastWasLabel{false}; + for (auto &op : operations) { + std::visit(Co::visitors{ + [&](const Fl::IndirectGotoOp &oper) { + genMLIR(ad, oper); + lastWasLabel = false; + }, + [&](const Fl::ActionOp &oper) { + noInsPt = false; + genMLIR(ad, oper); + lastWasLabel = false; + }, + [&](const Fl::LabelOp &oper) { + genMLIR(lastWasLabel, oper); + lastWasLabel = true; + }, + [&](const Fl::BeginOp &oper) { + noInsPt = false; + genMLIR(oper); + lastWasLabel = true; + }, + [&](const auto &oper) { + noInsPt = false; + genMLIR(oper); + lastWasLabel = false; + }, + }, + op.u); + } + if (build().getInsertionBlock()) { + // FIXME: assuming type of '() -> ()' + build().create(toLocation()); + } +} + +/// Translate the routine to MLIR +template +void MLIRConverter::translateRoutine( + const A &routine, const std::string &name, const Se::Symbol *funcSym) { + M::FuncOp func{getNamedFunction(name)}; + if (!func) { + // get arguments and return type if any, otherwise just use empty vectors + llvm::SmallVector args; + llvm::SmallVector results; + if (funcSym) { + if (auto *details{funcSym->detailsIf()}) { + for (auto a : details->dummyArgs()) { + auto type{translateSymbolToFIRType(&mlirContext, a)}; + args.push_back(fir::ReferenceType::get(type)); + } + if (details->isFunction()) { + // FIXME: handle subroutines that return magic values + auto *result{&details->result()}; + results.push_back(translateSymbolToFIRType(&mlirContext, result)); + } + } else { + llvm::errs() << "Symbol: " << funcSym->name().ToString() << " @ " + << funcSym->details().index() << '\n'; + assert(false && "symbol misidentified by front-end"); + } + } + auto funcTy{M::FunctionType::get(args, results, &mlirContext)}; + func = createFunction(getMod(), name, funcTy); + } + func.addEntryBlock(); + builder_ = std::make_unique(func); + build().setInsertionPointToStart(&func.front()); + if (funcSym) { + auto *entryBlock{&func.front()}; + if (auto *details{funcSym->detailsIf()}) { + for (const auto &v : + llvm::zip(details->dummyArgs(), entryBlock->getArguments())) { + symbolMap.addSymbol(std::get<0>(v), std::get<1>(v)); + } + } else { + llvm::errs() << "Symbol: " << funcSym->name().ToString() << " @ " + << funcSym->details().index() << '\n'; + assert(false && "symbol misidentified by front-end"); + } + } + AnalysisData ad; + std::list operations; + CreateFlatIR(routine, operations, ad); + genMLIR(ad, operations); + finalizeQueued(); +} + +M::DialectRegistration FIROps; + +} // namespace + +void Br::crossBurnsideBridge(BurnsideBridge &bridge, const Pa::Program &prg) { + MLIRConverter converter{bridge}; + Walk(prg, converter); +} + +std::unique_ptr Br::LLVMBridge(M::ModuleOp &module) { + return M::translateModuleToLLVMIR(module); +} + +void Br::BurnsideBridge::parseSourceFile(llvm::SourceMgr &srcMgr) { + module_ = M::parseSourceFile(srcMgr, context_.get()); + if (validModule()) { + // symbols are added by ModuleManager ctor + manager_.reset(new M::ModuleManager(getModule())); + } +} + +Br::BurnsideBridge::BurnsideBridge( + const Co::IntrinsicTypeDefaultKinds &defaultKinds) + : defaultKinds_{defaultKinds} { + context_ = std::make_unique(); + module_ = M::OwningModuleRef{ + M::ModuleOp::create(M::UnknownLoc::get(context_.get()))}; + manager_ = std::make_unique(getModule()); +} + +void Br::instantiateBurnsideBridge( + const Co::IntrinsicTypeDefaultKinds &defaultKinds) { + auto p{BurnsideBridge::create(defaultKinds)}; + bridgeInstance.swap(p); +} + +BurnsideBridge &Br::getBridge() { return *bridgeInstance.get(); } + +const common::IntrinsicTypeDefaultKinds &Br::getDefaultKinds() { + return getBridge().getDefaultKinds(); +} diff --git a/lib/burnside/bridge.h b/lib/burnside/bridge.h new file mode 100644 index 000000000000..edd2b0b60376 --- /dev/null +++ b/lib/burnside/bridge.h @@ -0,0 +1,93 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_BRIDGE_H_ +#define FORTRAN_BURNSIDE_BRIDGE_H_ + +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Module.h" +#include + +// Implement the burnside bridge from Fortran to MLIR +// https://github.com/tensorflow/mlir + +namespace Fortran::common { +class IntrinsicTypeDefaultKinds; +} + +namespace Fortran::parser { +struct Program; +} + +namespace llvm { +class Module; +class SourceMgr; +} + +namespace Fortran::burnside { + +/// An instance of BurnsideBridge is a singleton that owns the state of the +/// bridge +class BurnsideBridge { +public: + static std::unique_ptr create( + const common::IntrinsicTypeDefaultKinds &defaultKinds) { + BurnsideBridge *p = new BurnsideBridge{defaultKinds}; + return std::unique_ptr{p}; + } + + mlir::MLIRContext &getMLIRContext() { return *context_.get(); } + mlir::ModuleManager &getManager() { return *manager_.get(); } + mlir::ModuleOp getModule() { return module_.get(); } + + void parseSourceFile(llvm::SourceMgr &); + + const common::IntrinsicTypeDefaultKinds &getDefaultKinds() { + return defaultKinds_; + } + + bool validModule() { return getModule(); } + +private: + explicit BurnsideBridge( + const common::IntrinsicTypeDefaultKinds &defaultKinds); + BurnsideBridge() = delete; + BurnsideBridge(const BurnsideBridge &) = delete; + + const common::IntrinsicTypeDefaultKinds &defaultKinds_; + std::unique_ptr context_; + mlir::OwningModuleRef module_; + std::unique_ptr manager_; +}; + +/// Cross the bridge from the Fortran parse-tree, etc. to FIR+OpenMP+MLIR +void crossBurnsideBridge( + BurnsideBridge &bridge, const parser::Program &program); + +/// Bridge from MLIR to LLVM-IR +std::unique_ptr LLVMBridge(mlir::ModuleOp &module); + +/// instantiate the BURNSIDE bridge singleton +void instantiateBurnsideBridge( + const common::IntrinsicTypeDefaultKinds &defaultKinds); + +/// access to the default kinds class (for MLIR bridge) +const common::IntrinsicTypeDefaultKinds &getDefaultKinds(); + +/// get the burnside bridge singleton +BurnsideBridge &getBridge(); + +} // Fortran::burnside + +#endif // FORTRAN_BURNSIDE_BRIDGE_H_ diff --git a/lib/burnside/builder.cc b/lib/burnside/builder.cc new file mode 100644 index 000000000000..67e37cd36ec3 --- /dev/null +++ b/lib/burnside/builder.cc @@ -0,0 +1,56 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "builder.h" +#include "bridge.h" +#include "fe-helper.h" +#include "llvm/ADT/StringRef.h" +#include "mlir/IR/Module.h" +#include "mlir/IR/Value.h" + +namespace Br = Fortran::burnside; +namespace M = mlir; +namespace Se = Fortran::semantics; + +using namespace Fortran; +using namespace Fortran::burnside; + +// This will need to be extended to consider the type of what is being mangled +std::string Br::applyNameMangling(llvm::StringRef parserName) { + // FIXME: this is fake for now, add type info, etc. + return "_Qp_"s + parserName.str(); +} + +M::FuncOp Br::createFunction( + M::ModuleOp module, const std::string &name, M::FunctionType funcTy) { + M::MLIRContext *ctxt{module.getContext()}; + auto func{M::FuncOp::create(dummyLoc(ctxt), name, funcTy)}; + module.push_back(func); + return func; +} + +M::FuncOp Br::getNamedFunction(llvm::StringRef name) { + return getBridge().getManager().lookupSymbol(name); +} + +// symbol map: {Symbol* -> Value*} + +void Br::SymMap::addSymbol(const Se::Symbol *symbol, M::Value *value) { + sMap.try_emplace(symbol, value); +} + +M::Value *Br::SymMap::lookupSymbol(const Se::Symbol *symbol) { + auto iter{sMap.find(symbol)}; + return (iter == sMap.end()) ? nullptr : iter->second; +} diff --git a/lib/burnside/canonicalize.cc b/lib/burnside/canonicalize.cc new file mode 100644 index 000000000000..91524fe6de19 --- /dev/null +++ b/lib/burnside/canonicalize.cc @@ -0,0 +1,619 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "canonicalize.h" +#include "builder.h" +#include "fe-helper.h" +#include "fir/Dialect.h" +#include "fir/FIROps.h" +#include "fir/Type.h" +#include "runtime.h" +#include "../evaluate/fold.h" +#include "../evaluate/real.h" +#include "../semantics/expression.h" +#include "../semantics/symbol.h" +#include "../semantics/type.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/raw_ostream.h" +#include "mlir/Dialect/AffineOps/AffineOps.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/StandardOps/Ops.h" +#include "mlir/IR/Attributes.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Module.h" +#include "mlir/IR/Operation.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/IR/Value.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Transforms/DialectConversion.h" +#include "mlir/Transforms/Passes.h" + +namespace Br = Fortran::burnside; +namespace Co = Fortran::common; +namespace Ev = Fortran::evaluate; +namespace L = llvm; +namespace M = mlir; +namespace Pa = Fortran::parser; +namespace Se = Fortran::semantics; + +using namespace Fortran; +using namespace Fortran::burnside; + +namespace { + +#define TODO() \ + assert(false); \ + return {} + +/// Lowering of Fortran::evaluate::Expr expressions +class ExprLowering { + M::Location location; + M::OpBuilder &builder; + const SomeExpr &expr; + SymMap &symMap; + + M::Location getLoc() { return location; } + + /// Convert parser's INTEGER relational operators to MLIR. TODO: using + /// unordered, but we may want to cons ordered in certain situation. + static M::CmpIPredicate translateRelational(Co::RelationalOperator rop) { + switch (rop) { + case Co::RelationalOperator::LT: return M::CmpIPredicate::SLT; + case Co::RelationalOperator::LE: return M::CmpIPredicate::SLE; + case Co::RelationalOperator::EQ: return M::CmpIPredicate::EQ; + case Co::RelationalOperator::NE: return M::CmpIPredicate::NE; + case Co::RelationalOperator::GT: return M::CmpIPredicate::SGT; + case Co::RelationalOperator::GE: return M::CmpIPredicate::SGE; + } + assert(false && "unhandled INTEGER relational operator"); + return {}; + } + + /// Convert parser's REAL relational operators to MLIR. TODO: using + /// unordered, but we may want to cons ordered in certain situation. + static M::CmpFPredicate translateFloatRelational(Co::RelationalOperator rop) { + switch (rop) { + case Co::RelationalOperator::LT: return M::CmpFPredicate::ULT; + case Co::RelationalOperator::LE: return M::CmpFPredicate::ULE; + case Co::RelationalOperator::EQ: return M::CmpFPredicate::UEQ; + case Co::RelationalOperator::NE: return M::CmpFPredicate::UNE; + case Co::RelationalOperator::GT: return M::CmpFPredicate::UGT; + case Co::RelationalOperator::GE: return M::CmpFPredicate::UGE; + } + assert(false && "unhandled REAL relational operator"); + return {}; + } + + /// Generate an integral constant of `value` + template + M::Value *genIntegerConstant(M::MLIRContext *context, std::int64_t value) { + M::Type type{M::IntegerType::get(KIND * 8, context)}; + auto attr{builder.getIntegerAttr(type, value)}; + auto res{builder.create(getLoc(), type, attr)}; + return res.getResult(); + } + + /// Generate a logical/boolean constant of `value` + template + M::Value *genLogicalConstant(M::MLIRContext *context, bool value) { + auto attr{builder.getBoolAttr(value)}; + M::Type logTy{fir::LogicalType::get(context, KIND)}; + auto res{builder.create(getLoc(), logTy, attr)}; + return res.getResult(); + } + + template + M::Value *genRealConstant(M::MLIRContext *context, const L::APFloat &value) { + M::Type fltTy{convertReal(KIND, context)}; + auto attr{builder.getFloatAttr(fltTy, value)}; + auto res{builder.create(getLoc(), fltTy, attr)}; + return res.getResult(); + } + + M::Type getSomeKindInteger() { + return M::IndexType::get(builder.getContext()); + } + + template + M::Value *createBinaryOp(const A &ex, M::Value *lhs, M::Value *rhs) { + assert(lhs && rhs && "argument did not lower"); + auto x = builder.create(getLoc(), lhs, rhs); + return x.getResult(); + } + template + M::Value *createBinaryOp(const A &ex, M::Value *rhs) { + return createBinaryOp(ex, genval(ex.left()), rhs); + } + template M::Value *createBinaryOp(const A &ex) { + return createBinaryOp(ex, genval(ex.left()), genval(ex.right())); + } + + M::FuncOp getFunction(RuntimeEntryCode callee, M::FunctionType funTy) { + auto name{getRuntimeEntryName(callee)}; + auto module{getModule(&builder)}; + if (M::FuncOp func{getNamedFunction(name)}) { + return func; + } + return createFunction(module, name, funTy); + } + + // FIXME binary operation :: ('a, 'a) -> 'a + template M::FunctionType createFunctionType() { + if constexpr (TC == IntegerCat) { + M::Type output{M::IntegerType::get(KIND, builder.getContext())}; + L::SmallVector inputs; + inputs.push_back(output); + inputs.push_back(output); + return M::FunctionType::get(inputs, output, builder.getContext()); + } else if constexpr (TC == RealCat) { + M::Type output{convertReal(KIND, builder.getContext())}; + L::SmallVector inputs; + inputs.push_back(output); + inputs.push_back(output); + return M::FunctionType::get(inputs, output, builder.getContext()); + } else { + assert(false); + return {}; + } + } + + /// Create a call to a Fortran runtime entry point + template + M::Value *createBinaryFIRTCall(const A &ex, RuntimeEntryCode callee) { + L::SmallVector operands; + operands.push_back(genval(ex.left())); + operands.push_back(genval(ex.right())); + M::FunctionType funTy = createFunctionType(); + auto func{getFunction(callee, funTy)}; + auto x{builder.create(getLoc(), func, operands)}; + return x.getResult(0); // FIXME + } + + template + M::Value *createCompareOp( + const A &ex, M::CmpIPredicate pred, M::Value *lhs, M::Value *rhs) { + assert(lhs && rhs && "argument did not lower"); + auto x = builder.create(getLoc(), pred, lhs, rhs); + return x.getResult(); + } + template + M::Value *createCompareOp(const A &ex, M::CmpIPredicate pred) { + return createCompareOp( + ex, pred, genval(ex.left()), genval(ex.right())); + } + template + M::Value *createFltCmpOp( + const A &ex, M::CmpFPredicate pred, M::Value *lhs, M::Value *rhs) { + assert(lhs && rhs && "argument did not lower"); + auto x = builder.create(getLoc(), pred, lhs, rhs); + return x.getResult(); + } + template + M::Value *createFltCmpOp(const A &ex, M::CmpFPredicate pred) { + return createFltCmpOp( + ex, pred, genval(ex.left()), genval(ex.right())); + } + + M::Value *gen(const Se::Symbol *sym) { + // FIXME: not all symbols are local + return createTemporary(getLoc(), builder, symMap, + translateSymbolToFIRType(builder.getContext(), sym), sym); + } + M::Value *gendef(const Se::Symbol *sym) { return gen(sym); } + M::Value *genval(const Se::Symbol *sym) { + return builder.create(getLoc(), gen(sym)); + } + + M::Value *genval(const Ev::BOZLiteralConstant &) { TODO(); } + M::Value *genval(const Ev::ProcedureRef &) { TODO(); } + M::Value *genval(const Ev::ProcedureDesignator &) { TODO(); } + M::Value *genval(const Ev::NullPointer &) { TODO(); } + M::Value *genval(const Ev::StructureConstructor &) { TODO(); } + M::Value *genval(const Ev::ImpliedDoIndex &) { TODO(); } + M::Value *genval(const Ev::DescriptorInquiry &) { TODO(); } + template M::Value *genval(const Ev::TypeParamInquiry &) { + TODO(); + } + template M::Value *genval(const Ev::ComplexComponent &) { + TODO(); + } + template + M::Value *genval(const Ev::Negate> &) { + TODO(); + } + template + M::Value *genval(const Ev::Add> &op) { + if constexpr (TC == IntegerCat) { + return createBinaryOp(op); + } else if constexpr (TC == RealCat) { + return createBinaryOp(op); + } else { + TODO(); + } + } + template + M::Value *genval(const Ev::Subtract> &op) { + if constexpr (TC == IntegerCat) { + return createBinaryOp(op); + } else if constexpr (TC == RealCat) { + return createBinaryOp(op); + } else { + TODO(); + } + } + template + M::Value *genval(const Ev::Multiply> &op) { + if constexpr (TC == IntegerCat) { + return createBinaryOp(op); + } else if constexpr (TC == RealCat) { + return createBinaryOp(op); + } else { + TODO(); + } + } + template + M::Value *genval(const Ev::Divide> &op) { + if constexpr (TC == IntegerCat) { + return createBinaryOp(op); + } else if constexpr (TC == RealCat) { + return createBinaryOp(op); + } else if constexpr (TC == ComplexCat) { + return createBinaryFIRTCall(op, FIRT_CDIV); + } else { + TODO(); + } + } + template + M::Value *genval(const Ev::Power> &op) { + if constexpr (TC == IntegerCat) { + return createBinaryFIRTCall(op, FIRT_POW); + } else { + TODO(); + } + } + template + M::Value *genval(const Ev::RealToIntPower> &) { + TODO(); + } + template M::Value *genval(const Ev::ComplexConstructor &) { + TODO(); + } + template M::Value *genval(const Ev::Concat &op) { + return createBinaryFIRTCall(op, FIRT_CONCAT); + } + + /// MIN and MAX operations + template + M::Value *genval(const Ev::Extremum> &op) { + if constexpr (TC == IntegerCat) { + return createBinaryFIRTCall( + op, op.ordering == Ev::Ordering::Greater ? FIRT_MAX : FIRT_MIN); + } else { + TODO(); + } + } + + template M::Value *genval(const Ev::SetLength &) { TODO(); } + + template + M::Value *genval(const Ev::Relational> &op) { + if constexpr (TC == IntegerCat) { + return createCompareOp(op, translateRelational(op.opr)); + } else if constexpr (TC == RealCat) { + return createFltCmpOp(op, translateFloatRelational(op.opr)); + } else { + TODO(); + } + } + M::Value *genval(const Ev::Relational &op) { + return std::visit([&](const auto &x) { return genval(x); }, op.u); + } + + template + M::Value *genval(const Ev::Convert, TC2> &convert) { + auto ty{genTypeFromCategoryAndKind(builder.getContext(), TC1, KIND)}; + return builder.create(getLoc(), ty, genval(convert.left())); + } + template M::Value *genval(const Ev::Parentheses &) { TODO(); } + template M::Value *genval(const Ev::Not &op) { + auto *context{builder.getContext()}; + return createBinaryOp(op, genLogicalConstant(context, 1)); + } + + template M::Value *genval(const Ev::LogicalOperation &op) { + switch (op.logicalOperator) { + case Ev::LogicalOperator::And: return createBinaryOp(op); + case Ev::LogicalOperator::Or: return createBinaryOp(op); + case Ev::LogicalOperator::Eqv: + return createCompareOp(op, M::CmpIPredicate::EQ); + case Ev::LogicalOperator::Neqv: + return createCompareOp(op, M::CmpIPredicate::NE); + } + assert(false && "unhandled logical operation"); + return {}; + } + + template + M::Value *genval(const Ev::Constant> &con) { + if constexpr (TC == IntegerCat) { + auto opt{con.GetScalarValue()}; + if (opt.has_value()) + return genIntegerConstant(builder.getContext(), opt->ToInt64()); + assert(false && "integer constant has no value"); + return {}; + } else if constexpr (TC == LogicalCat) { + auto opt{con.GetScalarValue()}; + if (opt.has_value()) + return genLogicalConstant(builder.getContext(), opt->IsTrue()); + assert(false && "logical constant has no value"); + return {}; + } else if constexpr (TC == RealCat) { + auto opt{con.GetScalarValue()}; + if (opt.has_value()) { + std::string str{opt.value().DumpHexadecimal()}; + if constexpr (KIND == 2) { + L::APFloat floatVal{L::APFloatBase::IEEEhalf(), str}; + return genRealConstant(builder.getContext(), floatVal); + } else if constexpr (KIND == 4) { + L::APFloat floatVal{L::APFloatBase::IEEEsingle(), str}; + return genRealConstant(builder.getContext(), floatVal); + } else if constexpr (KIND == 10) { + L::APFloat floatVal{L::APFloatBase::x87DoubleExtended(), str}; + return genRealConstant(builder.getContext(), floatVal); + } else if constexpr (KIND == 16) { + L::APFloat floatVal{L::APFloatBase::IEEEquad(), str}; + return genRealConstant(builder.getContext(), floatVal); + } else { + // convert everything else to double + L::APFloat floatVal{L::APFloatBase::IEEEdouble(), str}; + return genRealConstant(builder.getContext(), floatVal); + } + } + assert(false && "real constant has no value"); + return {}; + } else { + assert(false && "unhandled constant"); + return {}; + } + } + + template + M::Value *genval(const Ev::Constant> &con) { + if constexpr (TC == IntegerCat) { + auto opt = (*con).ToInt64(); + M::Type type{getSomeKindInteger()}; + auto attr{builder.getIntegerAttr(type, opt)}; + auto res{builder.create(getLoc(), type, attr)}; + return res.getResult(); + } else { + assert(false && "unhandled constant of unknown kind"); + return {}; + } + } + + template M::Value *genval(const Ev::ArrayConstructor &) { + TODO(); + } + M::Value *gen(const Ev::ComplexPart &) { TODO(); } + M::Value *gendef(const Ev::ComplexPart &cp) { return gen(cp); } + M::Value *genval(const Ev::ComplexPart &) { TODO(); } + M::Value *gen(const Ev::Substring &) { TODO(); } + M::Value *gendef(const Ev::Substring &ss) { return gen(ss); } + M::Value *genval(const Ev::Substring &) { TODO(); } + M::Value *genval(const Ev::Triplet &trip) { TODO(); } + + M::Value *genval(const Ev::Subscript &subs) { + return std::visit(Co::visitors{ + [&](const Ev::IndirectSubscriptIntegerExpr &x) { + return genval(x.value()); + }, + [&](const Ev::Triplet &x) { return genval(x); }, + }, + subs.u); + } + + M::Value *gen(const Ev::DataRef &dref) { + return std::visit([&](const auto &x) { return gen(x); }, dref.u); + } + M::Value *gendef(const Ev::DataRef &dref) { return gen(dref); } + M::Value *genval(const Ev::DataRef &dref) { + return std::visit([&](const auto &x) { return genval(x); }, dref.u); + } + + // Helper function to turn the left-recursive Component structure into a list. + // Returns the object used as the base coordinate for the component chain. + static const Ev::DataRef *reverseComponents( + const Ev::Component &cmpt, std::list &list) { + list.push_front(&cmpt); + return std::visit( + Co::visitors{ + [&](const Ev::Component &x) { return reverseComponents(x, list); }, + [&](auto &) { return &cmpt.base(); }, + }, + cmpt.base().u); + } + + // Return the coordinate of the component reference + M::Value *gen(const Ev::Component &cmpt) { + std::list list; + auto *base{reverseComponents(cmpt, list)}; + L::SmallVector coorArgs; + auto obj{gen(*base)}; + const Se::Symbol *sym{nullptr}; + for (auto *field : list) { + sym = &field->GetLastSymbol(); + auto name{sym->name().ToString()}; + coorArgs.push_back(builder.create(getLoc(), name)); + } + assert(sym && "no component(s)?"); + M::Type ty{translateSymbolToFIRType(builder.getContext(), sym)}; + ty = fir::ReferenceType::get(ty); + return builder.create(getLoc(), ty, obj, coorArgs); + } + M::Value *gendef(const Ev::Component &cmpt) { return gen(cmpt); } + M::Value *genval(const Ev::Component &cmpt) { + return builder.create(getLoc(), gen(cmpt)); + } + + // Determine the result type after removing `dims` dimensions from the array + // type `arrTy` + M::Type genSubType(M::Type arrTy, unsigned dims) { + if (auto memRef{arrTy.dyn_cast()}) { + if (dims < memRef.getRank()) { + auto shape{memRef.getShape()}; + llvm::SmallVector newShape; + // TODO: should we really remove rows here? + for (unsigned i = dims, e = memRef.getRank(); i < e; ++i) { + newShape.push_back(shape[i]); + } + return M::MemRefType::get(newShape, memRef.getElementType()); + } + return memRef.getElementType(); + } + auto unwrapTy{arrTy.cast().getEleTy()}; + auto seqTy{unwrapTy.cast()}; + auto shape = seqTy.getShape(); + if (shape.known) { + if (dims < shape.bounds.size()) { + fir::SequenceType::Bounds newBnds; + // follow Fortran semantics and remove columns + for (unsigned i = 0; i < dims; ++i) { + newBnds.push_back(shape.bounds[i]); + } + return fir::SequenceType::get({newBnds}, seqTy.getEleTy()); + } + } + return seqTy.getEleTy(); + } + + // Return the coordinate of the array reference + M::Value *gen(const Ev::ArrayRef &aref) { + M::Value *base; + if (aref.base().IsSymbol()) + base = gen(const_cast(&aref.base().GetFirstSymbol())); + else + base = gen(aref.base().GetComponent()); + llvm::SmallVector args; + for (auto &subsc : aref.subscript()) { + args.push_back(genval(subsc)); + } + auto ty{genSubType(base->getType(), args.size() - 1)}; + ty = fir::ReferenceType::get(ty); + return builder.create(getLoc(), ty, base, args); + } + M::Value *gendef(const Ev::ArrayRef &aref) { return gen(aref); } + M::Value *genval(const Ev::ArrayRef &aref) { + return builder.create(getLoc(), gen(aref)); + } + + // Return a coordinate of the coarray reference. This is necessary as a + // Component may have a CoarrayRef as its base coordinate. + M::Value *gen(const Ev::CoarrayRef &coref) { + // FIXME: need to visit the cosubscripts... + // return gen(coref.base()); + TODO(); + } + M::Value *gendef(const Ev::CoarrayRef &coref) { return gen(coref); } + M::Value *genval(const Ev::CoarrayRef &coref) { + return builder.create(getLoc(), gen(coref)); + } + + template M::Value *gen(const Ev::Designator &des) { + return std::visit([&](const auto &x) { return gen(x); }, des.u); + } + template M::Value *gendef(const Ev::Designator &des) { + return gen(des); + } + template M::Value *genval(const Ev::Designator &des) { + return std::visit([&](const auto &x) { return genval(x); }, des.u); + } + + // call a function + template M::Value *gen(const Ev::FunctionRef &funRef) { + TODO(); + } + template M::Value *gendef(const Ev::FunctionRef &funRef) { + return gen(funRef); + } + template M::Value *genval(const Ev::FunctionRef &funRef) { + TODO(); + } + + template M::Value *gen(const Ev::Expr &exp) { + // must be a designator or function-reference (R902) + return std::visit([&](const auto &e) { return gendef(e); }, exp.u); + } + template M::Value *gendef(const Ev::Expr &exp) { + return gen(exp); + } + template M::Value *genval(const Ev::Expr &exp) { + return std::visit([&](const auto &e) { return genval(e); }, exp.u); + } + + template M::Value *gendef(const A &) { + assert(false && "expression error"); + return {}; + } + +public: + explicit ExprLowering( + M::Location loc, M::OpBuilder &bldr, const SomeExpr &vop, SymMap &map) + : location{loc}, builder{bldr}, expr{vop}, symMap{map} {} + + /// Lower the expression `expr` into MLIR standard dialect + M::Value *gen() { return gen(expr); } + M::Value *genval() { return genval(expr); } +}; + +} // namespace + +M::Value *Br::createSomeExpression(M::Location loc, M::OpBuilder &builder, + const Ev::Expr &expr, SymMap &symMap) { + ExprLowering lower{loc, builder, expr, symMap}; + return lower.genval(); +} + +M::Value *Br::createSomeAddress(M::Location loc, M::OpBuilder &builder, + const Ev::Expr &expr, SymMap &symMap) { + ExprLowering lower{loc, builder, expr, symMap}; + return lower.gen(); +} + +/// Create a temporary variable +/// `symbol` will be nullptr for an anonymous temporary +M::Value *Br::createTemporary(M::Location loc, M::OpBuilder &builder, + SymMap &symMap, M::Type type, const Se::Symbol *symbol) { + if (symbol) + if (auto *val{symMap.lookupSymbol(symbol)}) { + if (auto *op{val->getDefiningOp()}) return op->getResult(0); + return val; + } + auto insPt(builder.saveInsertionPoint()); + builder.setInsertionPointToStart(getEntryBlock(&builder)); + fir::AllocaOp ae; + assert(!type.dyn_cast() && "cannot be a reference"); + type = fir::ReferenceType::get(type); + if (symbol) { + ae = builder.create(loc, type, symbol->name().ToString()); + symMap.addSymbol(symbol, ae); + } else { + ae = builder.create(loc, type); + } + builder.restoreInsertionPoint(insPt); + return ae; +} diff --git a/lib/burnside/canonicalize.h b/lib/burnside/canonicalize.h new file mode 100644 index 000000000000..59d441b6fc94 --- /dev/null +++ b/lib/burnside/canonicalize.h @@ -0,0 +1,58 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_CANONICALIZE_H_ +#define FORTRAN_BURNSIDE_CANONICALIZE_H_ + +// In the Fortran::burnside namespace, the code will default follow the +// LLVM/MLIR coding standards + +namespace mlir { +class Location; +class OpBuilder; +class Type; +class Value; +} + +namespace fir { +class AllocaExpr; +} + +namespace Fortran { +namespace evaluate { +template class Expr; +class SomeType; +} // evaluate +namespace semantics { +class Symbol; +} + +namespace burnside { + +class SymMap; + +mlir::Value *createSomeExpression(mlir::Location loc, mlir::OpBuilder &builder, + const Fortran::evaluate::Expr &expr, + SymMap &symMap); +mlir::Value *createSomeAddress(mlir::Location loc, mlir::OpBuilder &builder, + const Fortran::evaluate::Expr &expr, + SymMap &symMap); + +mlir::Value *createTemporary(mlir::Location loc, mlir::OpBuilder &builder, + SymMap &symMap, mlir::Type type, const semantics::Symbol *symbol); + +} // burnside +} // Fortran + +#endif // FORTRAN_BURNSIDE_CANONICALIZE_H_ diff --git a/lib/burnside/common.h b/lib/burnside/common.h new file mode 100644 index 000000000000..2c3720c612ec --- /dev/null +++ b/lib/burnside/common.h @@ -0,0 +1,62 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_COMMON_H_ +#define FORTRAN_BURNSIDE_COMMON_H_ + +#include "../evaluate/expression.h" + +namespace Fortran::burnside { + +using Expression = evaluate::Expr; +using CallArguments = std::vector; + +enum InputOutputCallType { + InputOutputCallBackspace = 11, + InputOutputCallClose, + InputOutputCallEndfile, + InputOutputCallFlush, + InputOutputCallInquire, + InputOutputCallOpen, + InputOutputCallPrint, + InputOutputCallRead, + InputOutputCallRewind, + InputOutputCallWait, + InputOutputCallWrite, + InputOutputCallSIZE = InputOutputCallWrite - InputOutputCallBackspace + 1 +}; + +using IOCallArguments = CallArguments; + +enum RuntimeCallType { + RuntimeCallFailImage = 31, + RuntimeCallStop, + RuntimeCallPause, + RuntimeCallFormTeam, + RuntimeCallEventPost, + RuntimeCallEventWait, + RuntimeCallSyncAll, + RuntimeCallSyncImages, + RuntimeCallSyncMemory, + RuntimeCallSyncTeam, + RuntimeCallLock, + RuntimeCallUnlock, + RuntimeCallSIZE = RuntimeCallUnlock - RuntimeCallFailImage + 1 +}; + +using RuntimeCallArguments = CallArguments; + +} // Fortran::burnside + +#endif // FORTRAN_BURNSIDE_COMMON_H_ diff --git a/lib/burnside/expression.cc b/lib/burnside/expression.cc new file mode 100644 index 000000000000..367deb71c5fb --- /dev/null +++ b/lib/burnside/expression.cc @@ -0,0 +1,405 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "expression.h" +#include "builder.h" +#include "fe-helper.h" +#include "../semantics/expression.h" +#include "../semantics/symbol.h" +#include "../semantics/tools.h" +#include "../semantics/type.h" +#include "fir/Dialect.h" +#include "fir/Type.h" +#include "llvm/Support/raw_ostream.h" +#include "mlir/IR/Attributes.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/Module.h" +#include "mlir/IR/Operation.h" +#include "mlir/IR/StandardTypes.h" +#include "mlir/StandardOps/Ops.h" + +namespace Br = Fortran::burnside; +namespace Co = Fortran::common; +namespace Ev = Fortran::evaluate; +namespace M = mlir; +namespace Pa = Fortran::parser; +namespace Se = Fortran::semantics; + +using namespace Fortran; +using namespace Fortran::burnside; + +namespace { + +#undef TODO +#define TODO() assert(false) + +/// Collect the arguments and build the dictionary for FIR instructions with +/// embedded evaluate::Expr attributes. These arguments strictly conform to +/// data flow between Fortran expressions. +class TreeArgsBuilder { + M::OpBuilder *builder; + Args results; + Dict dictionary; + ExprType visited{ET_NONE}; + std::set keys; + SymMap &symbolMap; + + void addResult(M::Value *v, void *expr) { + const auto index{results.size()}; + results.push_back(v); + dictionary[index] = expr; + keys.insert(expr); + } + + void addResult(M::Value *v, const void *expr) { + addResult(v, const_cast(expr)); + } + + // FIXME: how do we map an evaluate::Expr to a source location? + M::Location dummyLoc() { return M::UnknownLoc::get(builder->getContext()); } + +public: + explicit TreeArgsBuilder(M::OpBuilder *builder, SymMap &map) + : builder{builder}, symbolMap{map} {} + + // FIXME: what we generate for a `Symbol` depends on the category of the + // symbol + void gen(const Se::Symbol *variable, M::Type ty, bool asAddr) { + if (keys.find(const_cast(variable)) == keys.end()) { + auto *addr = symbolMap.lookupSymbol(variable); + if (!addr) { + auto ip{builder->getInsertionBlock()}; + builder->setInsertionPointToStart(getEntryBlock(builder)); + addr = builder->create(dummyLoc(), ty); + symbolMap.addSymbol(variable, addr); + builder->setInsertionPointToEnd(ip); + } + if (asAddr) { + addResult(addr, variable); + } else { + llvm::SmallVector loadArg{addr}; + auto load{builder->create(dummyLoc(), loadArg, ty)}; + addResult(load.getResult(), variable); + } + } + visited = ET_Symbol; + } + + template void gen(const Ev::Designator &des, bool asAddr) { + std::visit(Co::visitors{ + [&](const Se::Symbol *x) { + auto *ctx{builder->getContext()}; + M::Type ty{translateDesignatorToFIRType(ctx, des)}; + gen(x, ty, asAddr); + }, + [&](const auto &x) { gen(x, asAddr); }, + }, + des.u); + } + + template + void gen(const Ev::Operation &op, bool asAddr) { + gen(op.left(), asAddr); + if constexpr (op.operands > 1) { + gen(op.right(), asAddr); + } + visited = ET_Operation; + } + + void gen(const Ev::DataRef &dref, bool asAddr) { + std::visit(Co::visitors{ + [&](const Se::Symbol *x) { + auto *ctx{builder->getContext()}; + M::Type ty{translateDataRefToFIRType(ctx, dref)}; + gen(x, ty, asAddr); + }, + [&](const auto &x) { gen(x, asAddr); }, + }, + dref.u); + } + + // Note that `NamedEntity` hides its variant member + void gen(const Ev::NamedEntity &ne, bool asAddr) { + if (ne.IsSymbol()) { + // GetFirstSymbol recovers the Symbol* variant + const Se::Symbol *x = &ne.GetFirstSymbol(); + auto *ctx{builder->getContext()}; + M::Type ty{translateSymbolToFIRType(ctx, x)}; + gen(x, ty, asAddr); + } else { + gen(ne.GetComponent(), asAddr); + } + } + + // Common pattern for all optional + template void gen(const std::optional &opt, bool asAddr) { + if (opt.has_value()) { + gen(opt.value(), asAddr); + } + } + + void gen(const Ev::Triplet &triplet, bool asAddr) { + gen(triplet.lower(), asAddr); + gen(triplet.upper(), asAddr); + gen(triplet.stride(), asAddr); + } + + /// Lower an array reference (implies address arithmetic) + /// + /// Visit each of the expressions in each dimension of the array access so as + /// to add them to the data flow. + void gen(const Ev::ArrayRef &aref, bool asAddr) { + // add the base + gen(aref.base(), true); + // add each expression + for (int i = 0, e = aref.size(); i < e; ++i) + std::visit(Co::visitors{ + [&](const Ev::Triplet &t) { gen(t, false); }, + [&](const Ev::IndirectSubscriptIntegerExpr &x) { + gen(x.value(), false); + }, + }, + aref.at(i).u); + visited = ET_ArrayRef; + } + + void gen(const Ev::NullPointer &, bool) { visited = ET_NullPointer; } + + // implies address arithmetic (and inter-image communication) + void gen(const Ev::CoarrayRef &coref, bool asAddr) { + for (auto *sym : coref.base()) { + gen(sym, translateSymbolToFIRType(builder->getContext(), sym), true); + } + for (auto &subs : coref.subscript()) { + std::visit(Co::visitors{ + [&](const Ev::Triplet &x) { gen(x, false); }, + [&](const Ev::IndirectSubscriptIntegerExpr &x) { + gen(x.value(), false); + }, + }, + subs.u); + } + for (auto &cosubs : coref.cosubscript()) { + gen(cosubs, false); + } + visited = ET_CoarrayRef; + } + + // implies address arithmetic + void gen(const Ev::Component &cmpt, bool) { + gen(cmpt.base(), true); + visited = ET_Component; + } + + void gen(const Ev::Substring &subs, bool asAddr) { + gen(subs.lower(), asAddr); + gen(subs.upper(), asAddr); + visited = ET_Substring; + } + + void gen(const Ev::ComplexPart &, bool) { visited = ET_ComplexPart; } + void gen(const Ev::ImpliedDoIndex &, bool) { + // do nothing + } + + void gen(const Ev::StructureConstructor &, bool) { + visited = ET_StructureCtor; + } + + // Skip over constants + void gen(const Ev::BOZLiteralConstant &, bool) { visited = ET_Constant; } + template void gen(const Ev::Constant &, bool) { + visited = ET_Constant; + } + + void gen(const Ev::DescriptorInquiry &, bool) { + TODO(); + visited = ET_DescriptorInquiry; + } + + template + void gen(const Ev::TypeParamInquiry &inquiry, bool asAddr) { + gen(inquiry.base(), asAddr); + visited = ET_TypeParamInquiry; + } + + void gen(const Ev::ProcedureRef &pref, bool asAddr) { + gen(pref.proc(), true); + for (auto &arg : pref.arguments()) { + if (arg.has_value()) { + auto &xarg{arg.value()}; + if (auto *x{xarg.UnwrapExpr()}) { + gen(*x, asAddr); + } else { + auto *sym{xarg.GetAssumedTypeDummy()}; + auto *ctx{builder->getContext()}; + gen(sym, translateSymbolToFIRType(ctx, sym), asAddr); + } + } + } + } + + void gen(const Ev::ProcedureDesignator &des, bool asAddr) { + std::visit(Co::visitors{ + [&](const Ev::SpecificIntrinsic &) { + // do nothing + }, + [&](const Se::Symbol *sym) { + auto *ctx{builder->getContext()}; + gen(sym, translateSymbolToFIRType(ctx, sym), true); + }, + [&](const Co::CopyableIndirection &x) { + gen(x.value(), true); + }, + }, + des.u); + } + + M::FunctionType genFunctionType(const Ev::ProcedureDesignator &proc) { + auto *ctx{builder->getContext()}; + M::Type ty{ + std::visit(Co::visitors{ + [&](const Ev::SpecificIntrinsic &) { + TODO(); /* FIXME */ + return M::Type{}; + }, + [&](const Se::Symbol *x) { + return translateSymbolToFIRType(ctx, x); + }, + [&](const Co::CopyableIndirection &x) { + auto *sym{&x.value().GetLastSymbol()}; + return translateSymbolToFIRType(ctx, sym); + }, + }, + proc.u)}; + return ty.cast(); + } + + /// Lower a function call + /// + /// A call is lowered to a sequence that evaluates the arguments and + /// passes them to a CallOp to the named function. + /// + /// TODO: The function needs to be name-mangled by the front-end or some + /// utility to avoid collisions. + /// + /// %54 = FIR.ApplyExpr(...) + /// %55 = FIR.ApplyExpr(...) + /// %56 = call(@target_func, %54, %55) + template void gen(const Ev::FunctionRef &funref, bool) { + // must be lowered to a call and data flow added for each actual arg + auto *context = builder->getContext(); + M::Location loc = M::UnknownLoc::get(context); // FIXME + + // lookup this function + llvm::StringRef callee = funref.proc().GetName(); + auto module{getModule(builder)}; + auto func = getNamedFunction(callee); + if (!func) { + // create new function + auto funTy{genFunctionType(funref.proc())}; + func = createFunction(module, callee, funTy); + } + assert(func); + + // build arguments + llvm::SmallVector inputs; + for (auto &arg : funref.arguments()) { + if (arg.has_value()) { + if (auto *aa{arg->UnwrapExpr()}) { + auto eops{translateSomeExpr(builder, aa, symbolMap)}; + auto aaType{ + FIRReferenceType::get(translateSomeExprToFIRType(context, aa))}; + M::Value *toLoc{nullptr}; + auto *defOp{getArgs(eops)[0]->getDefiningOp()}; + if (auto load = M::dyn_cast(defOp)) { + toLoc = load.getOperand(); + } else { + auto locate{builder->create( + loc, aa, getDict(eops), getArgs(eops), aaType)}; + toLoc = locate.getResult(); + } + inputs.push_back(toLoc); + } else { + auto *x{arg->GetAssumedTypeDummy()}; + // FIXME: can argument not be passed by reference? + gen(x, translateSymbolToFIRType(context, x), true); + } + } else { + assert(false && "argument is std::nullopt?"); + } + } + + // generate a call + auto call = builder->create(loc, func, inputs); + addResult(call.getResult(0), &funref); + visited = ET_FunctionRef; + } + + template void gen(const Ev::ImpliedDo &ido, bool asAddr) { + gen(ido.lower(), asAddr); + gen(ido.upper(), asAddr); + gen(ido.stride(), asAddr); + } + + template + void gen(const Ev::ArrayConstructor &arrayCtor, bool asAddr) { + for (auto i{arrayCtor.begin()}, end{arrayCtor.end()}; i != end; ++i) { + std::visit([&](auto &x) { gen(x, asAddr); }, i->u); + } + visited = ET_ArrayCtor; + } + + template void gen(const Ev::Relational &op, bool asAddr) { + gen(op.left(), asAddr); + gen(op.right(), asAddr); + visited = ET_Relational; + } + + void gen(const Ev::Relational &op, bool asAddr) { + std::visit([&](const auto &x) { gen(x, asAddr); }, op.u); + } + + template void gen(const Ev::Expr &expr, bool asAddr) { + std::visit([&](const auto &x) { gen(x, asAddr); }, expr.u); + } + + Args getResults() const { return results; } + Dict getDictionary() const { return dictionary; } + ExprType getExprType() const { return visited; } +}; + +// Builds the `([results], [inputs], {dict})` pair for constructing both +// `fir.apply_expr` and `fir.locate_expr` operations. +template +inline Values translateToFIR( + M::OpBuilder *bldr, const SomeExpr *exp, SymMap &map) { + TreeArgsBuilder ab{bldr, map}; + ab.gen(*exp, AddressResult); + return {ab.getResults(), ab.getDictionary(), ab.getExprType()}; +} + +} // namespace + +// `fir.apply_expr` builder +Values Br::translateSomeExpr( + M::OpBuilder *bldr, const SomeExpr *exp, SymMap &map) { + return translateToFIR(bldr, exp, map); +} + +// `fir.locate_expr` builder +Values Br::translateSomeAddrExpr( + M::OpBuilder *bldr, const SomeExpr *exp, SymMap &map) { + return translateToFIR(bldr, exp, map); +} diff --git a/lib/burnside/expression.h b/lib/burnside/expression.h new file mode 100644 index 000000000000..67ce921acd04 --- /dev/null +++ b/lib/burnside/expression.h @@ -0,0 +1,95 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_EXPRESSION_H_ +#define FORTRAN_BURNSIDE_EXPRESSION_H_ + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include +#include +#include + +/// Conversion of expressions with type Fortran::evaluate::Expr into the FIR +/// dialect of MLIR. +/// +/// Fortran expressions appear in syntactic contexts such as, for example, +/// assignment statements. The semantics of these expressions fall into two +/// cases: (1) an expression that computes a value and (2) an expression that +/// computes an address in storage. In the FIR dialect, case (1) is represented +/// as a "fir.apply_expr" operation. Case (2) is represented as a +/// "fir.locate_expr" operation. + +namespace mlir { +class MLIRContext; +class Value; +class OpBuilder; +} + +namespace Fortran::evaluate { +template class Expr; +struct SomeType; +} + +namespace Fortran::burnside { + +// In the Fortran::burnside namespace, the code will default follow the +// LLVM/MLIR coding standards + +class ApplyExpr; +class LocateExpr; + +enum ExprType { + ET_NONE, // Expr is unclassified type + ET_ArrayCtor, + ET_ArrayRef, + ET_CoarrayRef, + ET_ComplexPart, + ET_Component, + ET_Constant, + ET_DescriptorInquiry, + ET_FunctionRef, + ET_NullPointer, + ET_Operation, + ET_Relational, + ET_StructureCtor, + ET_Substring, + ET_Symbol, + ET_TypeParamInquiry +}; + +class SymMap; +using Args = llvm::SmallVector; +using Dict = std::map; +using Values = std::tuple; +using RewriteVals = mlir::Value *; +using OperandTy = llvm::ArrayRef; +using SomeExpr = evaluate::Expr; + +inline Args getArgs(const Values &values) { return std::get(values); } +inline Dict getDict(const Values &values) { return std::get(values); } +inline ExprType getExprType(const Values &values) { + return std::get(values); +} + +/// Convert an Expr in its implicit dataflow arguments +Values translateSomeExpr( + mlir::OpBuilder *bldr, const SomeExpr *exp, SymMap &map); + +Values translateSomeAddrExpr( + mlir::OpBuilder *bldr, const SomeExpr *exp, SymMap &map); + +} // Fortran::burnside + +#endif // FORTRAN_BURNSIDE_EXPRESSION_H_ diff --git a/lib/burnside/fe-helper.cc b/lib/burnside/fe-helper.cc new file mode 100644 index 000000000000..26b42edb5350 --- /dev/null +++ b/lib/burnside/fe-helper.cc @@ -0,0 +1,337 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fe-helper.h" +#include "bridge.h" +#include "fir/Type.h" +#include "../semantics/expression.h" +#include "../semantics/tools.h" +#include "../semantics/type.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/Location.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/StandardTypes.h" + +namespace Br = Fortran::burnside; +namespace Co = Fortran::common; +namespace Ev = Fortran::evaluate; +namespace M = mlir; +namespace Pa = Fortran::parser; +namespace Se = Fortran::semantics; + +using namespace Fortran; +using namespace Fortran::burnside; + +namespace { + +template bool isConstant(const Ev::Expr &e) { + return Ev::IsConstantExpr(SomeExpr{e}); +} + +template int64_t toConstant(const Ev::Expr &e) { + auto opt = Ev::ToInt64(e); + assert(opt.has_value() && "expression didn't resolve to a constant"); + return opt.value(); +} + +#undef TODO +#define TODO() assert(false) + +inline int defaultRealKind() { + return getDefaultKinds().GetDefaultKind(RealCat); +} + +inline int defaultIntegerKind() { + return getDefaultKinds().GetDefaultKind(IntegerCat); +} + +inline int defaultCharKind() { + return getDefaultKinds().GetDefaultKind(CharacterCat); +} + +inline int defaultLogicalKind() { + return getDefaultKinds().GetDefaultKind(LogicalCat); +} + +/// Recover the type of an evaluate::Expr and convert it to an +/// mlir::Type. The type returned can be a MLIR standard or FIR type. +class TypeBuilder { + M::MLIRContext *context; + +public: + explicit TypeBuilder(M::MLIRContext *context) : context{context} {} + + /// Create a FIR/MLIR real type + template static M::Type genReal(M::MLIRContext *context) { + if constexpr (KIND == 2) { + return M::FloatType::getF16(context); + } else if constexpr (KIND == 3) { + return M::FloatType::getBF16(context); + } else if constexpr (KIND == 4) { + return M::FloatType::getF32(context); + } else if constexpr (KIND == 8) { + return M::FloatType::getF64(context); + } else { + return fir::RealType::get(context, KIND); + } + } + + template M::Type genReal() { return genReal(context); } + + /// Create a FIR/MLIR real type from information at runtime + static M::Type genReal(int kind, M::MLIRContext *context) { + switch (kind) { + case 2: return genReal<2>(context); + case 3: return genReal<3>(context); + case 4: return genReal<4>(context); + case 8: return genReal<8>(context); + default: return fir::RealType::get(context, kind); + } + } + + M::Type genReal(int kind) { return genReal(kind, context); } + + static M::Type genType(M::MLIRContext *ctxt, Co::TypeCategory tc, int kind) { + switch (tc) { + case IntegerCat: return M::IntegerType::get(kind * 8, ctxt); + case RealCat: return genReal(kind, ctxt); + case ComplexCat: return M::ComplexType::get(genReal(kind, ctxt)); + case CharacterCat: return fir::CharacterType::get(ctxt, kind); + case LogicalCat: return fir::LogicalType::get(ctxt, kind); + default: break; + } + assert(false && "unhandled type category"); + return {}; + } + + static M::Type genType(M::MLIRContext *ctxt, Co::TypeCategory tc) { + switch (tc) { + case IntegerCat: return genType(ctxt, tc, defaultIntegerKind()); + case RealCat: return genType(ctxt, tc, defaultRealKind()); + case ComplexCat: return genType(ctxt, tc, defaultRealKind()); + case CharacterCat: return genType(ctxt, tc, defaultCharKind()); + case LogicalCat: return genType(ctxt, tc, defaultLogicalKind()); + case DerivedCat: return genType(ctxt, tc, 0); + default: break; + } + assert(false && "unknown type category"); + return {}; + } + + M::Type gen(const Ev::ImpliedDoIndex &) { + return genType(context, IntegerCat); + } + + template typename A, Co::TypeCategory TC> + M::Type gen(const A> &) { + return genType(context, TC); + } + + template M::Type gen(const Ev::TypeParamInquiry &) { + return genType(context, IntegerCat, KIND); + } + + template M::Type gen(const Ev::Relational &) { + return fir::LogicalType::get(context, 1); + } + + template typename A, Co::TypeCategory TC, int KIND> + M::Type gen(const A> &) { + return genType(context, TC, KIND); + } + + // breaks the conflict between A> and Expr deduction + template + M::Type gen(const Ev::Expr> &) { + return genType(context, TC, KIND); + } + + template M::Type genVariant(const A &variant) { + return std::visit([&](const auto &x) { return gen(x); }, variant.u); + } + + // breaks the conflict between A> and Expr deduction + template + M::Type gen(const Ev::Expr> &expr) { + return genVariant(expr); + } + + template M::Type gen(const Ev::Expr &expr) { + return genVariant(expr); + } + + M::Type gen(const Ev::DataRef &dref) { return genVariant(dref); } + + M::Type mkVoid() { return M::TupleType::get(context); } + + fir::SequenceType::Shape genSeqShape(const Se::Symbol *symbol) { + assert(symbol->IsObjectArray()); + fir::SequenceType::Bounds bounds; + auto &details = symbol->get(); + const auto size = details.shape().size(); + for (auto &ss : details.shape()) { + auto lb = ss.lbound(); + auto ub = ss.ubound(); + if (lb.isAssumed() && ub.isAssumed() && size == 1) { + return {}; + } + if (lb.isExplicit() && ub.isExplicit()) { + auto &lbv = lb.GetExplicit(); + auto &ubv = ub.GetExplicit(); + if (lbv.has_value() && ubv.has_value() && isConstant(lbv.value()) && + isConstant(ubv.value())) { + bounds.emplace_back( + true, toConstant(ubv.value()) - toConstant(lbv.value()) + 1); + } else { + bounds.emplace_back(false, 0); + } + } else { + bounds.emplace_back(false, 0); + } + } + return {bounds}; + } + + /// Type consing from a symbol. A symbol's type must be created from the type + /// discovered by the front-end at runtime. + M::Type gen(const Se::Symbol *symbol) { + if (auto *proc = symbol->detailsIf()) { + M::Type returnTy{mkVoid()}; + if (proc->isFunction()) { + returnTy = gen(&proc->result()); + } + // FIXME: handle alt-return + llvm::SmallVector inputTys; + for (auto *arg : proc->dummyArgs()) { + // FIXME: not all args are pass by ref + inputTys.emplace_back(fir::ReferenceType::get(gen(arg))); + } + return M::FunctionType::get(inputTys, returnTy, context); + } + M::Type returnTy{}; + if (auto *type{symbol->GetType()}) { + if (auto *tySpec{type->AsIntrinsic()}) { + int kind = toConstant(tySpec->kind()); + switch (tySpec->category()) { + case IntegerCat: { + returnTy = M::IntegerType::get(kind * 8, context); + } break; + case RealCat: { + returnTy = genReal(kind); + } break; + case ComplexCat: { + returnTy = M::ComplexType::get(genReal(kind)); + } break; + case CharacterCat: { + returnTy = fir::CharacterType::get(context, kind); + } break; + case LogicalCat: { + returnTy = fir::LogicalType::get(context, kind); + } break; + case DerivedCat: { + TODO(); + } break; + } + } else if (auto *tySpec{type->AsDerived()}) { + TODO(); + } else { + assert(false && "type spec not found"); + } + } else { + assert(false && "symbol has no type"); + } + if (symbol->IsObjectArray()) { + // FIXME: add bounds info + returnTy = fir::SequenceType::get(genSeqShape(symbol), returnTy); + } else if (Se::IsPointer(*symbol)) { + // FIXME: what about allocatable? + returnTy = fir::ReferenceType::get(returnTy); + } + return returnTy; + } + + fir::SequenceType::Shape trivialShape(int size) { + fir::SequenceType::Bounds bounds; + bounds.emplace_back(true, size); + return {bounds}; + } + + // some sequence of `n` bytes + M::Type gen(const Ev::StaticDataObject::Pointer &ptr) { + M::Type byteTy{M::IntegerType::get(8, context)}; + return fir::SequenceType::get(trivialShape(ptr->itemBytes()), byteTy); + } + + M::Type gen(const Ev::Substring &ss) { + return genVariant(ss.GetBaseObject()); + } + + M::Type genTypelessPtr() { return fir::ReferenceType::get(mkVoid()); } + M::Type gen(const Ev::NullPointer &) { return genTypelessPtr(); } + M::Type gen(const Ev::ProcedureRef &) { return genTypelessPtr(); } + M::Type gen(const Ev::ProcedureDesignator &) { return genTypelessPtr(); } + M::Type gen(const Ev::BOZLiteralConstant &) { return genTypelessPtr(); } + + M::Type gen(const Ev::ArrayRef &) { TODO(); } + M::Type gen(const Ev::CoarrayRef &) { TODO(); } + M::Type gen(const Ev::Component &) { TODO(); } + M::Type gen(const Ev::ComplexPart &) { TODO(); } + M::Type gen(const Ev::DescriptorInquiry &) { TODO(); } + M::Type gen(const Ev::StructureConstructor &) { TODO(); } +}; + +} // namespace + +/// Generate an unknown location +M::Location Br::dummyLoc(M::MLIRContext *ctxt) { + return M::UnknownLoc::get(ctxt); +} + +// What do we need to convert a CharBlock to actual source locations? +// FIXME: replace with a map from a provenance to a source location +M::Location Br::parserPosToLoc( + M::MLIRContext &context, const Pa::CharBlock &position) { + return dummyLoc(&context); +} + +M::Type Br::genTypeFromCategoryAndKind( + M::MLIRContext *ctxt, Co::TypeCategory tc, int kind) { + return TypeBuilder::genType(ctxt, tc, kind); +} + +M::Type Br::genTypeFromCategory(M::MLIRContext *ctxt, Co::TypeCategory tc) { + return TypeBuilder::genType(ctxt, tc); +} + +M::Type Br::translateDataRefToFIRType( + M::MLIRContext *context, const Ev::DataRef &dataRef) { + return TypeBuilder{context}.gen(dataRef); +} + +// Builds the FIR type from an instance of SomeExpr +M::Type Br::translateSomeExprToFIRType( + M::MLIRContext *context, const SomeExpr *expr) { + return TypeBuilder{context}.gen(*expr); +} + +// This entry point avoids gratuitously wrapping the Symbol instance in layers +// of Expr that will then be immediately peeled back off and discarded. +M::Type Br::translateSymbolToFIRType( + M::MLIRContext *context, const Se::Symbol *symbol) { + return TypeBuilder{context}.gen(symbol); +} + +M::Type Br::convertReal(int KIND, M::MLIRContext *context) { + return TypeBuilder::genReal(KIND, context); +} diff --git a/lib/burnside/fe-helper.h b/lib/burnside/fe-helper.h new file mode 100644 index 000000000000..05df769ab3d3 --- /dev/null +++ b/lib/burnside/fe-helper.h @@ -0,0 +1,98 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_FE_HELPER_H_ +#define FORTRAN_BURNSIDE_FE_HELPER_H_ + +/// Traversal and coversion of various Fortran::parser data structures into the +/// FIR dialect of MLIR. These traversals are isolated in this file to hopefully +/// make maintenance easier. + +#include "../common/Fortran.h" +#include "mlir/IR/Types.h" + +namespace mlir { +class Location; +class MLIRContext; +class Type; +} + +namespace Fortran::evaluate { +struct DataRef; +template class Designator; +template class Expr; +template struct SomeKind; +struct SomeType; +template class Type; +} + +namespace Fortran::parser { +class CharBlock; +} + +namespace Fortran::semantics { +class Symbol; +} + +namespace Fortran::burnside { + +using SomeExpr = evaluate::Expr; + +constexpr common::TypeCategory IntegerCat{common::TypeCategory::Integer}; +constexpr common::TypeCategory RealCat{common::TypeCategory::Real}; +constexpr common::TypeCategory ComplexCat{common::TypeCategory::Complex}; +constexpr common::TypeCategory CharacterCat{common::TypeCategory::Character}; +constexpr common::TypeCategory LogicalCat{common::TypeCategory::Logical}; +constexpr common::TypeCategory DerivedCat{common::TypeCategory::Derived}; + +// In the Fortran::burnside namespace, the code will default follow the +// LLVM/MLIR coding standards + +mlir::Location dummyLoc(mlir::MLIRContext *ctxt); + +/// Translate a CharBlock position to (source-file, line, column) +mlir::Location parserPosToLoc( + mlir::MLIRContext &context, const parser::CharBlock &position); + +mlir::Type genTypeFromCategoryAndKind( + mlir::MLIRContext *ctxt, common::TypeCategory tc, int kind); +mlir::Type genTypeFromCategory( + mlir::MLIRContext *ctxt, common::TypeCategory tc); + +mlir::Type translateDataRefToFIRType( + mlir::MLIRContext *ctxt, const evaluate::DataRef &dataRef); + +template +inline mlir::Type translateDesignatorToFIRType(mlir::MLIRContext *ctxt, + const evaluate::Designator> &) { + return genTypeFromCategoryAndKind(ctxt, TC, KIND); +} + +template +inline mlir::Type translateDesignatorToFIRType(mlir::MLIRContext *ctxt, + const evaluate::Designator> &) { + return genTypeFromCategory(ctxt, TC); +} + +mlir::Type translateSomeExprToFIRType( + mlir::MLIRContext *ctxt, const SomeExpr *expr); + +mlir::Type translateSymbolToFIRType( + mlir::MLIRContext *ctxt, const semantics::Symbol *symbol); + +mlir::Type convertReal(int KIND, mlir::MLIRContext *context); + +} // Fortran::burnside + +#endif // FORTRAN_BURNSIDE_FE_HELPER_H_ diff --git a/lib/burnside/flattened.cc b/lib/burnside/flattened.cc index 387cf3f377d4..57afc150ac45 100644 --- a/lib/burnside/flattened.cc +++ b/lib/burnside/flattened.cc @@ -26,39 +26,39 @@ LabelBuilder::LabelBuilder() : referenced(32), counter{0u} {} LabelRef LabelBuilder::getNext() { LabelRef next{counter++}; - auto cap{referenced.size()}; + auto cap{referenced.capacity()}; if (cap < counter) { - referenced.resize(2 * cap); + referenced.reserve(2 * cap); } - referenced.reset(next); + referenced.resize(counter, false); return next; } -void LabelBuilder::setReferenced(LabelRef label) { - CHECK(label < referenced.getBitCapacity()); - referenced.set(label); +void LabelBuilder::setReferenced(LabelRef label) { + CHECK(label < referenced.size()); + referenced[label] = true; } bool LabelBuilder::isReferenced(LabelRef label) const { - CHECK(label < referenced.getBitCapacity()); - return referenced.test(label); + CHECK(label < referenced.size()); + return referenced[label]; } LabelOp::LabelOp(LabelBuilder &builder) - : builder{builder}, label{builder.getNext()} {} + : builder_{builder}, label_{builder.getNext()} {} LabelOp::LabelOp(const LabelOp &that) - : builder{that.builder}, label{that.label} {} + : builder_{that.builder_}, label_{that.label_} {} LabelOp &LabelOp::operator=(const LabelOp &that) { - CHECK(&builder == &that.builder); - label = that.label; + CHECK(&builder_ == &that.builder_); + label_ = that.label_; return *this; } -void LabelOp::setReferenced() const { builder.setReferenced(label); } +void LabelOp::setReferenced() const { builder_.setReferenced(label_); } -bool LabelOp::isReferenced() const { return builder.isReferenced(label); } +bool LabelOp::isReferenced() const { return builder_.isReferenced(label_); } static void AddAssign(AnalysisData &ad, const semantics::Symbol *symbol, const parser::Label &label) { @@ -257,16 +257,15 @@ static std::list getAltReturnLabels(const parser::Call &call) { } static LabelRef NearestEnclosingDoConstruct(AnalysisData &ad) { - for (auto iterator{ad.constructContextStack.rbegin()}, - endIterator{ad.constructContextStack.rend()}; + for (auto iterator{ad.nameStack.rbegin()}, endIterator{ad.nameStack.rend()}; iterator != endIterator; ++iterator) { auto labelReference{std::get<2>(*iterator)}; - if (labelReference != UnspecifiedLabel) { + if (labelReference != unspecifiedLabel) { return labelReference; } } assert(false && "CYCLE|EXIT not in loop"); - return UnspecifiedLabel; + return unspecifiedLabel; } template std::string GetSource(const A *s) { @@ -301,16 +300,16 @@ void Op::Build(std::list &ops, }, [&](const common::Indirection &s) { ops.emplace_back(GotoOp{s.value(), - s.value().v ? std::get<2>(FindStack(ad.constructContextStack, - &s.value().v.value())) - : NearestEnclosingDoConstruct(ad), + s.value().v + ? std::get<2>(FindStack(ad.nameStack, &s.value().v.value())) + : NearestEnclosingDoConstruct(ad), ec.source}); }, [&](const common::Indirection &s) { ops.emplace_back(GotoOp{s.value(), - s.value().v ? std::get<1>(FindStack(ad.constructContextStack, - &s.value().v.value())) - : NearestEnclosingDoConstruct(ad), + s.value().v + ? std::get<1>(FindStack(ad.nameStack, &s.value().v.value())) + : NearestEnclosingDoConstruct(ad), ec.source}); }, [&](const common::Indirection &s) { @@ -440,8 +439,7 @@ struct ControlFlowAnalyzer { std::list ops; LabelOp label{buildNewLabel()}; const parser::Name *name{getName(construct)}; - ad.constructContextStack.emplace_back( - name, GetLabelRef(label), UnspecifiedLabel); + ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); appendIfLabeled(std::get<0>(construct.t), ops); ops.emplace_back(BeginOp{construct}); ControlFlowAnalyzer cfa{ops, ad}; @@ -450,7 +448,7 @@ struct ControlFlowAnalyzer { appendIfLabeled(std::get<2>(construct.t), ops); ops.emplace_back(EndOp{construct}); linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); + ad.nameStack.pop_back(); return false; } @@ -465,8 +463,7 @@ struct ControlFlowAnalyzer { std::get>(construct.t) .statement.v}; const parser::Name *name{optName ? &*optName : nullptr}; - ad.constructContextStack.emplace_back( - name, GetLabelRef(label), UnspecifiedLabel); + ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); appendIfLabeled( std::get>(construct.t), ops); ops.emplace_back(BeginOp{construct}); @@ -477,12 +474,10 @@ struct ControlFlowAnalyzer { ops.emplace_back(EndOp{construct}); ops.emplace_back(label); linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); + ad.nameStack.pop_back(); return false; } - /// `DO` constructs can be lowered to `fir.loop` if they meet some - /// constraints, otherwise they are lowered to a CFG. bool Pre(const parser::DoConstruct &construct) { std::list ops; LabelOp backedgeLab{buildNewLabel()}; @@ -491,8 +486,7 @@ struct ControlFlowAnalyzer { LabelOp exitLab{buildNewLabel()}; const parser::Name *name{getName(construct)}; LabelRef exitOpRef{GetLabelRef(exitLab)}; - ad.constructContextStack.emplace_back( - name, exitOpRef, GetLabelRef(incrementLab)); + ad.nameStack.emplace_back(name, exitOpRef, GetLabelRef(incrementLab)); appendIfLabeled( std::get>(construct.t), ops); ops.emplace_back(BeginOp{construct}); @@ -513,20 +507,17 @@ struct ControlFlowAnalyzer { ops.emplace_back(EndOp{construct}); ops.emplace_back(exitLab); linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); + ad.nameStack.pop_back(); return false; } - /// `IF` constructs can be lowered to `fir.where` if they meet some - /// constraints, otherwise they are lowered to a CFG. bool Pre(const parser::IfConstruct &construct) { std::list ops; LabelOp thenLab{buildNewLabel()}; LabelOp elseLab{buildNewLabel()}; LabelOp exitLab{buildNewLabel()}; const parser::Name *name{getName(construct)}; - ad.constructContextStack.emplace_back( - name, GetLabelRef(exitLab), UnspecifiedLabel); + ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel); appendIfLabeled( std::get>(construct.t), ops); ops.emplace_back(BeginOp{construct}); @@ -567,7 +558,7 @@ struct ControlFlowAnalyzer { std::get>(construct.t), ops); ops.emplace_back(EndOp{construct}); linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); + ad.nameStack.pop_back(); return false; } @@ -576,8 +567,7 @@ struct ControlFlowAnalyzer { std::list ops; LabelOp exitLab{buildNewLabel()}; const parser::Name *name{getName(construct)}; - ad.constructContextStack.emplace_back( - name, GetLabelRef(exitLab), UnspecifiedLabel); + ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel); appendIfLabeled(std::get<0>(construct.t), ops); ops.emplace_back(BeginOp{construct}); const auto N{std::get>(construct.t).size()}; @@ -607,7 +597,7 @@ struct ControlFlowAnalyzer { appendIfLabeled(std::get<2>(construct.t), ops); ops.emplace_back(EndOp{construct}); linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); + ad.nameStack.pop_back(); return false; } @@ -619,8 +609,7 @@ struct ControlFlowAnalyzer { std::list ops; LabelOp label{buildNewLabel()}; const parser::Name *name{getName(c)}; - ad.constructContextStack.emplace_back( - name, GetLabelRef(label), UnspecifiedLabel); + ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); appendIfLabeled( std::get>(c.t), ops); ops.emplace_back(BeginOp{c}); @@ -634,7 +623,7 @@ struct ControlFlowAnalyzer { std::get>(c.t), ops); ops.emplace_back(EndOp{c}); linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); + ad.nameStack.pop_back(); return false; } @@ -642,8 +631,7 @@ struct ControlFlowAnalyzer { std::list ops; LabelOp label{buildNewLabel()}; const parser::Name *name{getName(construct)}; - ad.constructContextStack.emplace_back( - name, GetLabelRef(label), UnspecifiedLabel); + ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); appendIfLabeled( std::get>(construct.t), ops); @@ -655,7 +643,7 @@ struct ControlFlowAnalyzer { std::get>(construct.t), ops); ops.emplace_back(EndOp{construct}); linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); + ad.nameStack.pop_back(); return false; } diff --git a/lib/burnside/flattened.h b/lib/burnside/flattened.h index 7d07d477968f..b22607f4cf7b 100644 --- a/lib/burnside/flattened.h +++ b/lib/burnside/flattened.h @@ -15,9 +15,9 @@ #ifndef FORTRAN_BURNSIDE_FLATTENED_H_ #define FORTRAN_BURNSIDE_FLATTENED_H_ +#include "common.h" #include "mixin.h" #include "../parser/parse-tree.h" -#include "llvm/ADT/BitVector.h" #include #include #include @@ -30,14 +30,12 @@ struct AnalysisData; namespace flat { -/// This is a flattened, linearized representation of the parse -/// tree. It captures the executable specification of the input -/// program. The flattened IR can be used to construct FIR. -/// -/// [Coding style](https://llvm.org/docs/CodingStandards.html) +// This is a flattened, linearized representation of the parse tree. It captures +// the executable specification of the input program. The flattened IR can be +// used to construct the Fortran IR. using LabelRef = unsigned; -constexpr LabelRef UnspecifiedLabel{UINT_MAX}; +constexpr LabelRef unspecifiedLabel{UINT_MAX}; using Location = parser::CharBlock; struct LabelBuilder; @@ -49,12 +47,12 @@ struct LabelOp { LabelOp &operator=(const LabelOp &that); void setReferenced() const; bool isReferenced() const; - LabelRef get() const { return label; } + LabelRef get() const { return label_; } operator LabelRef() const { return get(); } private: - LabelBuilder &builder; - LabelRef label; + LabelBuilder &builder_; + LabelRef label_; }; struct ArtificialJump {}; @@ -100,10 +98,10 @@ struct ConditionalGotoOp struct IndirectGotoOp { explicit IndirectGotoOp( const semantics::Symbol *symbol, std::vector &&labelRefs) - : labelRefs{labelRefs}, symbol{symbol} {} + : symbol{symbol}, labelRefs{labelRefs} {} - std::vector labelRefs; const semantics::Symbol *symbol; + std::vector labelRefs; }; // intrinsic IO operations can return with an implied multi-way branch @@ -120,6 +118,7 @@ struct SwitchIOOp std::optional endLab = std::nullopt) : SumTypeCopyMixin{&io}, next{next}, source{source}, errLabel{errLab}, eorLabel{eorLab}, endLabel{endLab} {} + LabelRef next; Location source; std::optional errLabel; @@ -201,7 +200,7 @@ struct LabelBuilder { LabelRef getNext(); void setReferenced(LabelRef label); bool isReferenced(LabelRef label) const; - llvm::BitVector referenced; + std::vector referenced; unsigned counter; }; @@ -212,11 +211,10 @@ std::vector GetAssign( } // namespace flat -// Collection of data maintained internally by the flattening algorithm struct AnalysisData { std::map labelMap; std::vector> - constructContextStack; + nameStack; flat::LabelBuilder labelBuilder; std::map> assignMap; }; diff --git a/lib/burnside/mixin.h b/lib/burnside/mixin.h index f24ccc2b1b08..853b7734fcc8 100644 --- a/lib/burnside/mixin.h +++ b/lib/burnside/mixin.h @@ -20,8 +20,6 @@ // better to think of these as "included in" a class, rather than as an // "inherited from" base class. -/// [Coding style](https://llvm.org/docs/CodingStandards.html) - #include "llvm/ADT/ilist.h" #include #include diff --git a/lib/burnside/runtime.cc b/lib/burnside/runtime.cc new file mode 100644 index 000000000000..dc14ce642918 --- /dev/null +++ b/lib/burnside/runtime.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "runtime.h" +#include "fir/Type.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "mlir/IR/StandardTypes.h" +#include "mlir/IR/Types.h" +#include + +namespace Br = Fortran::burnside; + +using namespace Fortran; +using namespace Fortran::burnside; + +namespace { + +using FuncPointer = llvm::SmallVector (*)( + mlir::MLIRContext *, int kind); + +// FIXME: these are wrong and just serving as temporary placeholders +using Cplx = mlir::IntegerType; +using Intopt = mlir::IntegerType; +using TypeDesc = mlir::IntegerType; + +template +void consType( + llvm::SmallVector &types, mlir::MLIRContext *ctx, int kind) { + if constexpr (std::is_same_v) { + types.emplace_back(A::get(kind, ctx)); + } else if constexpr (std::is_same_v) { + // FIXME: this is wrong + assert(false); + types.emplace_back(mlir::IntegerType::get(kind, ctx)); + } else { + types.emplace_back(A::get(ctx, kind)); + } + if constexpr (sizeof...(B) > 0) { + consType(types, ctx, kind); + } +} + +template +llvm::SmallVector consType(mlir::MLIRContext *ctx, int kind) { + llvm::SmallVector types; + if constexpr (sizeof...(A) > 0) { + consType(types, ctx, kind); + } else { + (void)ctx; + (void)kind; + } + return types; +} + +#define DEFINE_RUNTIME_ENTRY(A, B, C, D) "Fortran_" B, +char const *const RuntimeEntryNames[FIRT_LAST_ENTRY_CODE] = { +#include "runtime.def" +}; + +#define UNPACK_TYPES(...) __VA_ARGS__ +#define DEFINE_RUNTIME_ENTRY(A, B, C, D) consType, +FuncPointer RuntimeEntryInputType[FIRT_LAST_ENTRY_CODE] = { +#include "runtime.def" +}; + +#define DEFINE_RUNTIME_ENTRY(A, B, C, D) consType, +FuncPointer RuntimeEntryResultType[FIRT_LAST_ENTRY_CODE] = { +#include "runtime.def" +}; +#undef UNPACK_TYPES + +} // namespace + +llvm::StringRef Br::getRuntimeEntryName(RuntimeEntryCode code) { + assert(code < FIRT_LAST_ENTRY_CODE); + return {RuntimeEntryNames[code]}; +} + +mlir::FunctionType Br::getRuntimeEntryType( + RuntimeEntryCode code, mlir::MLIRContext &mlirContext, int kind) { + assert(code < FIRT_LAST_ENTRY_CODE); + return mlir::FunctionType::get( + RuntimeEntryInputType[code](&mlirContext, kind), + RuntimeEntryResultType[code](&mlirContext, kind), &mlirContext); +} + +mlir::FunctionType Br::getRuntimeEntryType(RuntimeEntryCode code, + mlir::MLIRContext &mlirContext, int inpKind, int resKind) { + assert(code < FIRT_LAST_ENTRY_CODE); + return mlir::FunctionType::get( + RuntimeEntryInputType[code](&mlirContext, inpKind), + RuntimeEntryResultType[code](&mlirContext, resKind), &mlirContext); +} diff --git a/lib/burnside/runtime.def b/lib/burnside/runtime.def new file mode 100644 index 000000000000..9121321fc80a --- /dev/null +++ b/lib/burnside/runtime.def @@ -0,0 +1,42 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Define the various runtime entry points. At this point, these are entirely +// bogons as we don't have a runtime library nor even a definition for one. + +// Fortran execution control primitive ops +DEFINE_RUNTIME_ENTRY(STOP, "stop", (Intopt, Intopt), ()) +DEFINE_RUNTIME_ENTRY(ERROR_STOP, "error_stop", (Intopt, Intopt), ()) +DEFINE_RUNTIME_ENTRY(FAIL_IMAGE, "fail_image", (), ()) + +// INTEGER ops +DEFINE_RUNTIME_ENTRY(MAX, "int_max", (mlir::IntegerType, mlir::IntegerType), (mlir::IntegerType)) +DEFINE_RUNTIME_ENTRY(MIN, "int_min", (mlir::IntegerType, mlir::IntegerType), (mlir::IntegerType)) +DEFINE_RUNTIME_ENTRY(POW, "int_pow", (mlir::IntegerType, mlir::IntegerType), (mlir::IntegerType)) + +// COMPLEX ops +DEFINE_RUNTIME_ENTRY(CDIV, "complex_div", (Cplx, Cplx), (Cplx)) + +// CHARACTER ops +DEFINE_RUNTIME_ENTRY(CONCAT, "char_concat", (fir::CharacterType, fir::CharacterType), (fir::CharacterType)) + +// TYPE descriptor queries +DEFINE_RUNTIME_ENTRY(ISA_TYPE, "isa_type", (TypeDesc), (mlir::IntegerType)) +DEFINE_RUNTIME_ENTRY(ISA_SUBTYPE, "isa_subtype", (TypeDesc), (mlir::IntegerType)) + +// ARRAY descriptor queries +DEFINE_RUNTIME_ENTRY(GET_RANK, "get_rank", (TypeDesc), (mlir::IntegerType)) +DEFINE_RUNTIME_ENTRY(GET_ELETYPE, "get_element_type", (TypeDesc), (mlir::IntegerType)) + +#undef DEFINE_RUNTIME_ENTRY diff --git a/lib/burnside/runtime.h b/lib/burnside/runtime.h new file mode 100644 index 000000000000..5275f33b586c --- /dev/null +++ b/lib/burnside/runtime.h @@ -0,0 +1,49 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_RUNTIME_H_ +#define FORTRAN_BURNSIDE_RUNTIME_H_ + +#include + +namespace llvm { +class StringRef; +} +namespace mlir { +class FunctionType; +class MLIRContext; +} + +namespace Fortran::burnside { + +// In the Fortran::burnside namespace, the code will default follow the +// LLVM/MLIR coding standards + +#define DEFINE_RUNTIME_ENTRY(A, B, C, D) FIRT_##A, +enum RuntimeEntryCode { +#include "runtime.def" + FIRT_LAST_ENTRY_CODE +}; + +llvm::StringRef getRuntimeEntryName(RuntimeEntryCode code); + +mlir::FunctionType getRuntimeEntryType( + RuntimeEntryCode code, mlir::MLIRContext &mlirContext, int kind); + +mlir::FunctionType getRuntimeEntryType(RuntimeEntryCode code, + mlir::MLIRContext &mlirContext, int inpKind, int resKind); + +} // Fortran::burnside + +#endif // FORTRAN_BURNSIDE_RUNTIME_H_ diff --git a/lib/fir/Attribute.cpp b/lib/fir/Attribute.cpp new file mode 100644 index 000000000000..46a2eeac54c5 --- /dev/null +++ b/lib/fir/Attribute.cpp @@ -0,0 +1,178 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fir/Attribute.h" +#include "fir/Dialect.h" +#include "fir/Type.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "mlir/IR/AttributeSupport.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/Types.h" +#include "mlir/Parser.h" + +namespace L = llvm; +namespace M = mlir; + +using namespace fir; + +namespace { + +// Our very stripped down parser for Attributes. +// TODO: clean this up +class AttributeParser { +public: + AttributeParser(L::StringRef rawText) + : srcBuff{rawText}, srcPtr{rawText.begin()} {} + + M::Attribute parseAttribute(FIROpsDialect *dialect, M::Location loc) { + skipWhitespace(); + L::StringRef keyword{lexKeyword()}; + skipWhitespace(); + auto data{srcBuff.drop_front(srcPtr - srcBuff.begin())}; + if (keyword == ExactTypeAttr::getAttrName()) { + data = consumeAngles(loc, data); + M::Type type{M::parseType(data, dialect->getContext())}; + return ExactTypeAttr::get(type); + } + if (keyword == SubclassAttr::getAttrName()) { + data = consumeAngles(loc, data); + M::Type type{M::parseType(data, dialect->getContext())}; + return SubclassAttr::get(type); + } + if (keyword == PointIntervalAttr::getAttrName()) { + return PointIntervalAttr::get(dialect->getContext()); + } + if (keyword == LowerBoundAttr::getAttrName()) { + return LowerBoundAttr::get(dialect->getContext()); + } + if (keyword == UpperBoundAttr::getAttrName()) { + return UpperBoundAttr::get(dialect->getContext()); + } + if (keyword == ClosedIntervalAttr::getAttrName()) { + return ClosedIntervalAttr::get(dialect->getContext()); + } + L::Twine msg{"unknown FIR attribute: "}; + M::emitError(loc, msg.concat(srcBuff)); + return {}; + } + +private: + bool atEnd() const { return srcPtr == srcBuff.end(); } + + L::StringRef lexKeyword() { + const char *tokStart = srcPtr; + while (!atEnd() && (std::isalnum(*srcPtr) || *srcPtr == '_')) { + ++srcPtr; + } + return {tokStart, static_cast(srcPtr - tokStart)}; + } + + void skipWhitespace() { + while (!atEnd()) { + switch (*srcPtr) { + case ' ': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': ++srcPtr; continue; + default: break; + } + break; + } + } + + L::StringRef consumeAngles(M::Location loc, L::StringRef data) { + data = data.ltrim().rtrim(); + if (data.size() <= 2) { + M::emitError(loc, "expecting '<' type '>' in attribute"); + return {}; + } + if (data.front() != '<') { + M::emitError(loc, "expecting '<' in attribute"); + return {}; + } + data = data.drop_front(1); + if (data.back() != '>') { + M::emitError(loc, "expecting '>' in attribute"); + return {}; + } + data = data.drop_back(1); + return data; + } + + L::StringRef srcBuff; + const char *srcPtr; +}; + +} // namespace + +namespace fir { +namespace detail { + +/// An attribute representing a reference to a type. +struct TypeAttributeStorage : public M::AttributeStorage { + using KeyTy = M::Type; + + TypeAttributeStorage(M::Type value) : value(value) {} + + /// Key equality function. + bool operator==(const KeyTy &key) const { return key == value; } + + /// Construct a new storage instance. + static TypeAttributeStorage *construct( + M::AttributeStorageAllocator &allocator, KeyTy key) { + return new (allocator.allocate()) + TypeAttributeStorage(key); + } + + M::Type value; +}; +} // detail + +ExactTypeAttr ExactTypeAttr::get(M::Type value) { + return Base::get(value.getContext(), FIR_EXACTTYPE, value); +} + +M::Type ExactTypeAttr::getType() const { return getImpl()->value; } + +SubclassAttr SubclassAttr::get(M::Type value) { + return Base::get(value.getContext(), FIR_SUBCLASS, value); +} + +M::Type SubclassAttr::getType() const { return getImpl()->value; } + +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()); +} + +M::Attribute parseFirAttribute(FIROpsDialect *dialect, L::StringRef rawText, + M::Type type, M::Location loc) { + AttributeParser parser{rawText}; + return parser.parseAttribute(dialect, loc); +} + +} // fir diff --git a/lib/fir/CMakeLists.txt b/lib/fir/CMakeLists.txt new file mode 100644 index 000000000000..253b2f8bd7dc --- /dev/null +++ b/lib/fir/CMakeLists.txt @@ -0,0 +1,45 @@ +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_library(FIR + Attribute.cpp + Dialect.cpp + FIROps.cpp + LLVMConverter.cpp + MemToReg.cpp + StdConverter.cpp + Type.cpp +) + +add_dependencies(FIR FIROpsIncGen) + +target_link_libraries(FIR + MLIRTargetLLVMIR + MLIRTargetLLVMIRModuleTranslation + MLIREDSC + MLIRExecutionEngine + MLIRParser + MLIRSupport + MLIRStandardToLLVM + MLIRTransforms + LLVMAsmParser + LLVMAsmPrinter + LLVMRemarks +) + +install (TARGETS FIR + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) diff --git a/lib/fir/Dialect.cpp b/lib/fir/Dialect.cpp new file mode 100644 index 000000000000..0f29ce87b1f1 --- /dev/null +++ b/lib/fir/Dialect.cpp @@ -0,0 +1,182 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fir/Dialect.h" +#include "fir/Attribute.h" +#include "fir/FIROps.h" +#include "fir/Type.h" +#include "../evaluate/expression.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/IR/StandardTypes.h" + +namespace M = mlir; + +using namespace fir; + +namespace { + +template +void selectBuild(M::OpBuilder *builder, M::OperationState *result, + M::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(M::MLIRContext *ctx) + : M::Dialect("fir", ctx) { + addTypes(); + addAttributes(); + addOperations(); +} + +// anchor the class vtable +fir::FIROpsDialect::~FIROpsDialect() {} + +M::Type fir::FIROpsDialect::parseType( + llvm::StringRef rawData, M::Location loc) const { + return parseFirType(const_cast(this), rawData, loc); +} + +void printBounds(llvm::raw_ostream &os, const SequenceType::Bounds &bounds) { + char ch = '<'; + for (auto &b : bounds) { + if (b.known) { + os << ch << b.bound; + } else { + os << ch << '?'; + } + ch = 'x'; + } +} + +void fir::FIROpsDialect::printType(M::Type ty, llvm::raw_ostream &os) const { + if (auto type = ty.dyn_cast()) { + os << "ref<"; + type.getEleTy().print(os); + os << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "logical<" << type.getFKind() << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "real<" << type.getFKind() << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "char<" << type.getFKind() << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "tdesc<"; + type.getOfTy().print(os); + os << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "field"; + } else if (auto type = ty.dyn_cast()) { + os << "box<"; + type.getEleTy().print(os); + os << '>'; + } else if (auto type = ty.dyn_cast()) { + auto eleTy = type.getEleTy().cast(); + os << "boxchar<" << eleTy.getFKind() << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "boxproc<"; + type.getEleTy().print(os); + os << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "dims<" << type.getRank() << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "array"; + auto shape = type.getShape(); + if (shape.known) { + printBounds(os, shape.bounds); + } else { + os << "<*"; + } + os << ':'; + type.getEleTy().print(os); + os << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "heap<"; + type.getEleTy().print(os); + os << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "ptr<"; + type.getEleTy().print(os); + os << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "type<" << type.getName(); + 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 << '}'; + } + os << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "int<" << type.getFKind() << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "complex<" << type.getFKind() << '>'; + } else { + assert(false); + } +} + +M::Attribute fir::FIROpsDialect::parseAttribute( + llvm::StringRef rawText, M::Type type, M::Location loc) const { + return parseFirAttribute( + const_cast(this), rawText, type, loc); +} + +void fir::FIROpsDialect::printAttribute( + M::Attribute attr, llvm::raw_ostream &os) const { + if (auto exact = attr.dyn_cast()) { + os << fir::ExactTypeAttr::getAttrName() << '<' << exact.getType() << '>'; + } else if (auto sub = attr.dyn_cast()) { + os << fir::SubclassAttr::getAttrName() << '<' << sub.getType() << '>'; + } 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 { + assert(false); + } +} diff --git a/lib/fir/FIROps.cpp b/lib/fir/FIROps.cpp new file mode 100644 index 000000000000..a923fbb31f32 --- /dev/null +++ b/lib/fir/FIROps.cpp @@ -0,0 +1,378 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fir/FIROps.h" +#include "fir/Attribute.h" +#include "fir/Type.h" +#include "mlir/Dialect/StandardOps/Ops.h" +#include "mlir/IR/StandardTypes.h" +#include "mlir/IR/SymbolTable.h" + +namespace L = llvm; +namespace M = mlir; + +namespace fir { + +// AllocaOp + +M::Type AllocaOp::getAllocatedType() { + return getType().cast().getEleTy(); +} + +// DispatchTableOp + +void DispatchTableOp::build(M::Builder *builder, M::OperationState *result, + L::StringRef name, M::Type type, L::ArrayRef attrs) { + result->addAttribute( + M::SymbolTable::getSymbolAttrName(), builder->getStringAttr(name)); + for (const auto &pair : attrs) { + result->addAttribute(pair.first, pair.second); + } +} + +M::ParseResult DispatchTableOp::parse( + M::OpAsmParser *parser, M::OperationState *result) { + // Parse the name as a symbol reference attribute. + SymbolRefAttr nameAttr; + if (parser->parseAttribute( + nameAttr, M::SymbolTable::getSymbolAttrName(), result->attributes)) + return failure(); + + // Convert the parsed name attr into a string attr. + result->attributes.back().second = + parser->getBuilder().getStringAttr(nameAttr.getValue()); + + // Parse the optional table body. + M::Region *body = result->addRegion(); + if (parser->parseOptionalRegion(*body, + L::ArrayRef{}, L::ArrayRef{})) + return M::failure(); + + ensureTerminator(*body, parser->getBuilder(), result->location); + return M::success(); +} + +void DispatchTableOp::print(M::OpAsmPrinter *p) { + auto tableName = + getAttrOfType(M::SymbolTable::getSymbolAttrName()).getValue(); + *p << getOperationName() << " @" << tableName; + + Region &body = getOperation()->getRegion(0); + if (!body.empty()) + p->printRegion(body, /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/false); +} + +M::LogicalResult DispatchTableOp::verify() { return M::success(); } + +M::Region &DispatchTableOp::front() { + return this->getOperation()->getRegion(0); +} + +void DispatchTableOp::appendTableEntry(M::Operation *op) { + assert(M::isa(*op) && "operation must be a DTEntryOp"); + front().front().push_back(op); +} + +M::ParseResult parseCallOp(M::OpAsmParser *parser, M::OperationState *result) { + M::FunctionType calleeType; + L::SmallVector operands; + M::OpAsmParser::OperandType callee; + auto calleeLoc = parser->getNameLoc(); + if (parser->parseOperand(callee)) { + M::SymbolRefAttr calleeAttr; + if (parser->parseAttribute(calleeAttr, "proc", result->attributes) || + parser->parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || + parser->parseOptionalAttributeDict(result->attributes) || + parser->parseColonType(calleeType) || + parser->addTypesToList(calleeType.getResults(), result->types) || + parser->resolveOperands( + operands, calleeType.getInputs(), calleeLoc, result->operands)) + return M::failure(); + } else { + result->attributes.push_back(parser->getBuilder().getNamedAttr( + "proc", parser->getBuilder().getSymbolRefAttr(""))); + if (parser->getCurrentLocation(&calleeLoc) || + parser->parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || + parser->parseOptionalAttributeDict(result->attributes) || + parser->parseColonType(calleeType) || + parser->resolveOperand(callee, calleeType, result->operands) || + parser->resolveOperands( + operands, calleeType.getInputs(), calleeLoc, result->operands) || + parser->addTypesToList(calleeType.getResults(), result->types)) + return M::failure(); + } + return M::success(); +} + +mlir::ParseResult parseDispatchOp( + mlir::OpAsmParser *parser, mlir::OperationState *result) { + M::FunctionType calleeType; + L::SmallVector operands; + auto calleeLoc = parser->getNameLoc(); + M::StringAttr calleeAttr; + if (parser->parseAttribute(calleeAttr, "proc", result->attributes) || + parser->parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || + parser->parseOptionalAttributeDict(result->attributes) || + parser->parseColonType(calleeType) || + parser->addTypesToList(calleeType.getResults(), result->types) || + parser->resolveOperands( + operands, calleeType.getInputs(), calleeLoc, result->operands)) + return M::failure(); + return M::success(); +} + +// GlobalOp + +void GlobalOp::build(M::Builder *builder, M::OperationState *result, + L::StringRef name, M::Type type, L::ArrayRef attrs) { + result->addAttribute(getTypeAttrName(), builder->getTypeAttr(type)); + result->addAttribute( + M::SymbolTable::getSymbolAttrName(), builder->getStringAttr(name)); + for (const auto &pair : attrs) { + result->addAttribute(pair.first, pair.second); + } +} + +M::ParseResult GlobalOp::parse( + M::OpAsmParser *parser, M::OperationState *result) { + // Parse the name as a symbol reference attribute. + SymbolRefAttr nameAttr; + if (parser->parseAttribute( + nameAttr, M::SymbolTable::getSymbolAttrName(), result->attributes)) + return failure(); + + auto &builder = parser->getBuilder(); + result->attributes.back().second = builder.getStringAttr(nameAttr.getValue()); + + M::Type globalType; + if (parser->parseColonType(globalType)) { + return M::failure(); + } + result->addAttribute(getTypeAttrName(), builder.getTypeAttr(globalType)); + + // Parse the optional initializer body. + M::Region *body = result->addRegion(); + if (parser->parseOptionalRegion(*body, + L::ArrayRef{}, L::ArrayRef{})) + return M::failure(); + + ensureTerminator(*body, builder, result->location); + return M::success(); +} + +void GlobalOp::print(M::OpAsmPrinter *p) { + auto varName = + getAttrOfType(M::SymbolTable::getSymbolAttrName()).getValue(); + *p << getOperationName() << " @" << varName << " : "; + p->printType(getType()); + Region &body = getOperation()->getRegion(0); + if (!body.empty()) + p->printRegion(body, /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/false); +} + +M::LogicalResult GlobalOp::verify() { return M::success(); } + +void GlobalOp::appendInitialValue(M::Operation *op) { + front().front().push_back(op); +} + +M::Region &GlobalOp::front() { return this->getOperation()->getRegion(0); } + +// LoadOp + +/// Get the element type of a reference like type; otherwise null +M::Type elementTypeOf(M::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 {}; +} + +M::ParseResult LoadOp::getElementOf(M::Type &ele, M::Type ref) { + if (M::Type r = elementTypeOf(ref)) { + ele = r; + return M::success(); + } + return M::failure(); +} + +// LoopOp + +void LoopOp::build(M::Builder *builder, M::OperationState *result, M::Value *lb, + M::Value *ub, L::ArrayRef step) { + if (step.empty()) + result->addOperands({lb, ub}); + else + result->addOperands({lb, ub, step[0]}); + M::Region *bodyRegion = result->addRegion(); + LoopOp::ensureTerminator(*bodyRegion, *builder, result->location); + bodyRegion->front().addArgument(builder->getIndexType()); +} + +M::ParseResult parseLoopOp(M::OpAsmParser *parser, M::OperationState *result) { + auto &builder = parser->getBuilder(); + M::OpAsmParser::OperandType inductionVariable, lb, ub, step; + // Parse the induction variable followed by '='. + if (parser->parseRegionArgument(inductionVariable) || parser->parseEqual()) + return M::failure(); + + // Parse loop bounds. + M::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 M::failure(); + if (parser->parseOptionalKeyword(LoopOp::getStepKeyword())) { + result->addAttribute(LoopOp::getStepKeyword(), + builder.getIntegerAttr(builder.getIndexType(), 1)); + } else if (parser->parseOperand(step) || + parser->resolveOperand(step, indexType, result->operands)) { + return M::failure(); + } + + if (parser->parseOptionalKeyword("unordered")) { + // ok + } else { + result->addAttribute("unordered", builder.getBoolAttr(true)); + } + + // Parse the body region. + M::Region *body = result->addRegion(); + if (parser->parseRegion(*body, inductionVariable, indexType)) + return M::failure(); + + fir::LoopOp::ensureTerminator(*body, builder, result->location); + + // Parse the optional attribute list. + if (parser->parseOptionalAttributeDict(result->attributes)) { + return M::failure(); + } + return M::success(); +} + +fir::LoopOp getForInductionVarOwner(M::Value *val) { + auto *ivArg = dyn_cast(val); + if (!ivArg) { + return fir::LoopOp(); + } + assert(ivArg->getOwner() && "unlinked block argument"); + auto *containingInst = ivArg->getOwner()->getParentOp(); + return dyn_cast_or_null(containingInst); +} + +// StoreOp + +M::Type StoreOp::elementType(M::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 {}; +} + +// WhereOp + +void WhereOp::build(M::Builder *builder, M::OperationState *result, + M::Value *cond, bool withElseRegion) { + result->addOperands(cond); + M::Region *thenRegion = result->addRegion(); + M::Region *elseRegion = result->addRegion(); + WhereOp::ensureTerminator(*thenRegion, *builder, result->location); + if (withElseRegion) + WhereOp::ensureTerminator(*elseRegion, *builder, result->location); +} + +M::ParseResult parseWhereOp(M::OpAsmParser *parser, M::OperationState *result) { + // Create the regions for 'then'. + result->regions.reserve(2); + M::Region *thenRegion = result->addRegion(); + M::Region *elseRegion = result->addRegion(); + + auto &builder = parser->getBuilder(); + M::OpAsmParser::OperandType cond; + M::Type i1Type = builder.getIntegerType(1); + if (parser->parseOperand(cond) || + parser->resolveOperand(cond, i1Type, result->operands)) + return M::failure(); + + if (parser->parseRegion(*thenRegion, {}, {})) { + return M::failure(); + } + WhereOp::ensureTerminator( + *thenRegion, parser->getBuilder(), result->location); + + if (!parser->parseOptionalKeyword("otherwise")) { + if (parser->parseRegion(*elseRegion, {}, {})) { + return M::failure(); + } + WhereOp::ensureTerminator( + *elseRegion, parser->getBuilder(), result->location); + } + + // Parse the optional attribute list. + if (parser->parseOptionalAttributeDict(result->attributes)) { + return M::failure(); + } + + return M::success(); +} + +M::ParseResult isValidCaseAttr(M::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 M::success(); + return M::failure(); +} + +unsigned getCaseArgumentOffset(L::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(); +} + +// Tablegen operators + +#define GET_OP_CLASSES +#include "fir/FIROps.cpp.inc" + +} // fir diff --git a/lib/fir/LLVMConverter.cpp b/lib/fir/LLVMConverter.cpp new file mode 100644 index 000000000000..c89e37414372 --- /dev/null +++ b/lib/fir/LLVMConverter.cpp @@ -0,0 +1,705 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fir/Tilikum/LLVMConverter.h" +#include "fir/Dialect.h" +#include "fir/FIROps.h" +#include "fir/Type.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Config/abi-breaking.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" +#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h" +#include "mlir/Dialect/AffineOps/AffineOps.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/StandardOps/Ops.h" +#include "mlir/IR/StandardTypes.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Target/LLVMIR.h" +#include "mlir/Transforms/DialectConversion.h" + +// This module performs the conversion of FIR operations to MLIR standard and/or +// LLVM-IR dialects. + +namespace L = llvm; +namespace M = mlir; + +using namespace fir; + +namespace { + +using SmallVecResult = L::SmallVector; +using OperandTy = L::ArrayRef; +using AttributeTy = L::ArrayRef; + +/// FIR type converter +/// This converts FIR types to LLVM types (for now) +class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { +public: + using LLVMTypeConverter::LLVMTypeConverter; + + // convert a front-end kind value to either a std or LLVM IR dialect type + static M::Type kindToRealType( + M::MLIRContext *ctx, L::LLVMContext &llvmCtx, KindTy kind) { + switch (kind) { + case 0: return M::FloatType::getF32(ctx); // FIXME: use defaulted kind + case 2: return M::FloatType::getF16(ctx); + case 3: return M::FloatType::getBF16(ctx); + case 4: return M::FloatType::getF32(ctx); + case 8: return M::FloatType::getF64(ctx); + case 10: + return M::LLVM::LLVMType::get(ctx, L::Type::getX86_FP80Ty(llvmCtx)); + case 16: return M::LLVM::LLVMType::get(ctx, L::Type::getFP128Ty(llvmCtx)); + } + assert(!kind && "unhandled kind"); + return {}; + } + + // lower the type descriptor + M::Type convertTypeDescType(M::MLIRContext *ctx) { + auto &llvmCtx = getLLVMContext(); + auto i64 = L::Type::getIntNTy(llvmCtx, 64); + return M::LLVM::LLVMType::get(ctx, i64->getPointerTo()); + } + + template M::Type convertPointerLike(A &ty) { + M::Type ele = ty.getEleTy(); + auto eleTy = convertType(ele); + if (ele.dyn_cast()) { + return eleTy; + } + auto *ptrTy = eleTy.cast().getUnderlyingType(); + return M::LLVM::LLVMType::get(ty.getContext(), ptrTy->getPointerTo()); + } + + /// Convert FIR types to LLVM IR dialect types + M::Type convertType(M::Type t) override { + auto &llvmCtx = getLLVMContext(); + if (auto box = t.dyn_cast()) { + // (buffer*, ele-size, rank, type-descriptor, attribute, [dims]) + L::SmallVector parts; + // buffer* + M::Type ele = box.getEleTy(); + auto *ctx = box.getContext(); + M::Type eleTy = convertType(ele); + L::Type *i64 = L::Type::getIntNTy(llvmCtx, 64); + if (ele.dyn_cast()) { + parts.push_back(eleTy); + } else { + auto *ptrTy = eleTy.cast().getUnderlyingType(); + parts.push_back(M::LLVM::LLVMType::get(ctx, ptrTy->getPointerTo())); + } + // ele-size + parts.push_back(M::LLVM::LLVMType::get(ctx, i64)); + // rank + parts.push_back(convertTypeDescType(ctx)); + // attribute + parts.push_back(M::LLVM::LLVMType::get(ctx, i64)); + // [(int,int,int)] + parts.push_back(M::LLVM::LLVMType::get( + ctx, L::ArrayType::get(i64, 3)->getPointerTo())); + // ... + return LLVMTypeConverter::convertType(M::TupleType::get(parts, ctx)); + } + if (auto boxchar = t.dyn_cast()) { + // (buffer*, buffer-size) + L::SmallVector parts; + // buffer* + parts.push_back(M::LLVM::LLVMType::get( + boxchar.getContext(), L::Type::getIntNTy(llvmCtx, 64))); + // ... + assert(false); + return t; // fixme + } + if (auto boxproc = t.dyn_cast()) { + // (function*, host-context*) + assert(false); + return t; // fixme + } + if (auto chr = t.dyn_cast()) { + L::Type *llTy = L::Type::getIntNTy(llvmCtx, chr.getSizeInBits()); + return M::LLVM::LLVMType::get(chr.getContext(), llTy); + } + if (auto cplx = t.dyn_cast()) { + M::Type realTy = + kindToRealType(cplx.getContext(), llvmCtx, cplx.getFKind()); + return LLVMTypeConverter::convertType(M::ComplexType::get(realTy)); + } + if (auto derived = t.dyn_cast()) { + assert(false); + return t; // fixme + } + if (auto dims = t.dyn_cast()) { + // [rank x ] + L::Type *i64 = L::Type::getIntNTy(llvmCtx, 64); + if (auto rank = dims.getRank()) { + return M::LLVM::LLVMType::get(dims.getContext(), + L::ArrayType::get(L::VectorType::get(i64, 3), rank)); + } + return M::LLVM::LLVMType::get( + dims.getContext(), L::VectorType::get(i64, 3)->getPointerTo()); + } + if (auto field = t.dyn_cast()) { + return M::LLVM::LLVMType::get( + field.getContext(), L::Type::getIntNTy(llvmCtx, 64)); + } + if (auto heap = t.dyn_cast()) { + return convertPointerLike(heap); + } + if (auto integer = t.dyn_cast()) { + L::Type *llTy = L::Type::getIntNTy(llvmCtx, integer.getSizeInBits()); + return M::LLVM::LLVMType::get(integer.getContext(), llTy); + } + if (auto log = t.dyn_cast()) { + L::Type *llTy = L::Type::getIntNTy(llvmCtx, log.getSizeInBits()); + return M::LLVM::LLVMType::get(log.getContext(), llTy); + } + if (auto pointer = t.dyn_cast()) { + return convertPointerLike(pointer); + } + if (auto real = t.dyn_cast()) { + M::Type realTy = + kindToRealType(real.getContext(), llvmCtx, real.getFKind()); + return LLVMTypeConverter::convertType(realTy); + } + if (auto ref = t.dyn_cast()) { + return convertPointerLike(ref); + } + if (auto seq = t.dyn_cast()) { + M::Type eleTy = convertType(seq.getEleTy()); + auto shape = seq.getShape(); + L::SmallVector memshape; + if (shape.known) { + for (auto bi : shape.bounds) { + if (bi.known) { + memshape.push_back(bi.bound); + } else { + memshape.push_back(-1); // unknown shape + } + } + } + std::reverse(memshape.begin(), memshape.end()); + return LLVMTypeConverter::convertType( + M::MemRefType::get(memshape, eleTy)); + } + if (auto tdesc = t.dyn_cast()) { + return convertTypeDescType(tdesc.getContext()); + } + return LLVMTypeConverter::convertType(t); + } +}; + +/// FIR conversion pattern template +template class FIROpConversion : public M::ConversionPattern { +public: + explicit FIROpConversion( + M::MLIRContext *ctx, FIRToLLVMTypeConverter &lowering) + : ConversionPattern(FromOp::getOperationName(), 1, ctx), + lowering(lowering) {} + +protected: + L::LLVMContext &getLLVMContext() const { return lowering.getLLVMContext(); } + M::LLVM::LLVMDialect *getDialect() const { return lowering.getDialect(); } + + FIRToLLVMTypeConverter &lowering; +}; + +// convert to LLVM IR dialect `alloca` +struct AllocaOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto alloc = M::cast(op); + rewriter.replaceOpWithNewOp( + op, lowering.convertType(alloc.getType()), operands, alloc.getAttrs()); + return matchSuccess(); + } +}; + +// convert to `call` to the runtime to `malloc` memory +struct AllocMemOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto heap = M::cast(op); + // FIXME: should be a call to malloc + rewriter.replaceOpWithNewOp( + op, lowering.convertType(heap.getType()), operands, heap.getAttrs()); + return matchSuccess(); + } +}; + +// convert value of from-type to value of to-type +struct ConvertOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto convert = M::cast(op); + M::Type fromTy = lowering.convertType(convert.value()->getType()); + M::Type toTy = lowering.convertType(convert.res()->getType()); + auto loc = op->getLoc(); + M::Value *op0 = operands[0]; + auto *fromLLVMTy = fromTy.cast().getUnderlyingType(); + auto *toLLVMTy = fromTy.cast().getUnderlyingType(); + M::Value *v = nullptr; + if (fromLLVMTy == toLLVMTy) { + rewriter.replaceOp(op, op0); + return matchSuccess(); + } + if (fromLLVMTy->isFloatingPointTy()) { + if (toLLVMTy->isIntegerTy()) { + v = rewriter.create(loc, toTy, op0); + } else if (toLLVMTy->isFloatingPointTy()) { + unsigned fromBits = fromLLVMTy->getIntegerBitWidth(); + unsigned toBits = toLLVMTy->getIntegerBitWidth(); + assert(fromBits != toBits); + if (fromBits > toBits) + v = rewriter.create(loc, toTy, op0); + else + v = rewriter.create(loc, toTy, op0); + } + } else if (fromLLVMTy->isIntegerTy()) { + if (toLLVMTy->isIntegerTy()) { + unsigned fromBits = fromLLVMTy->getIntegerBitWidth(); + unsigned toBits = toLLVMTy->getIntegerBitWidth(); + assert(fromBits != toBits); + if (fromBits > toBits) + v = rewriter.create(loc, toTy, op0); + else + v = rewriter.create(loc, toTy, op0); + } else if (toLLVMTy->isFloatingPointTy()) { + v = rewriter.create(loc, toTy, op0); + } else if (toLLVMTy->isPointerTy()) { + v = rewriter.create(loc, toTy, op0); + } + } else if (fromLLVMTy->isPointerTy()) { + if (toLLVMTy->isIntegerTy()) + v = rewriter.create(loc, toTy, op0); + } + if (v == nullptr) { + v = rewriter.create(loc, toTy, op0); + } + + if (auto fromInt = fromTy.dyn_cast()) { + if (auto toInt = toTy.dyn_cast()) { + M::Value *v; + if (fromInt.getIntOrFloatBitWidth() < toInt.getIntOrFloatBitWidth()) { + v = rewriter.create(loc, toInt, op0); + } else { + v = rewriter.create(loc, toInt, op0); + } + rewriter.replaceOp(op, v); + return matchSuccess(); + } + // FIXME -- finish implementation + } + assert(false); + return matchSuccess(); + } +}; + +// convert to reference to a reference to a subobject +struct CoordinateOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto coor = M::cast(op); + auto baseOp = coor.ref()->getDefiningOp(); + auto loc = coor.getLoc(); + if (auto box = M::dyn_cast(baseOp)) { + // FIXME: for now assume this is always an array + M::Value *v = rewriter.create( + loc, lowering.convertType(coor.getType()), box.memref(), operands); + rewriter.replaceOp(op, v); + return matchSuccess(); + } + assert(false); // FIXME + return matchSuccess(); + } +}; + +// convert a reference to an LLVM struct value +struct EmboxOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::Value *insertValue(M::Location loc, int index, M::Value *aggr, + M::Value *op, M::Type partTy, + M::ConversionPatternRewriter &rewriter) const { + L::SmallVector attrs; + attrs.push_back(rewriter.getI64IntegerAttr(index)); + return rewriter.create( + loc, partTy, aggr, op, rewriter.getArrayAttr(attrs)); + } + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto embox = M::cast(op); + auto *ctx = embox.getContext(); + M::Type convTy = + lowering.convertType(embox.getType().cast().getEleTy()); + L::Type *eleTy = + convTy.cast().getUnderlyingType()->getPointerTo(); + L::SmallVector members; + members.push_back(eleTy); + M::Type opTy = lowering.convertType(operands[1]->getType()); + L::Type *dimLLVMType = opTy.cast().getUnderlyingType(); + members.push_back(dimLLVMType); + L::Type *boxLLVMType = L::StructType::get(getLLVMContext(), members); + M::Type boxTy = M::LLVM::LLVMType::get(ctx, boxLLVMType); + auto loc = embox.getLoc(); + M::Value *u = rewriter.create( + loc, boxTy, L::ArrayRef{}); + auto *v = insertValue(loc, 0, u, operands[0], boxTy, rewriter); + M::Type dimTy = M::LLVM::LLVMType::get(ctx, dimLLVMType); + auto *w = insertValue(loc, 1, v, operands[1], dimTy, rewriter); + rewriter.replaceOp(op, w); + return matchSuccess(); + } +}; + +struct ExtractValueOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto extractVal = M::cast(op); + // FIXME + assert(false); + return matchSuccess(); + } +}; + +struct FreeMemOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::LLVM::LLVMType getVoidPtrType() const { + return M::LLVM::LLVMType::getInt8PtrTy(getDialect()); + } + + M::FuncOp genFreeFunc( + M::Operation *op, M::ConversionPatternRewriter &rewriter) const { + M::FuncOp freeFunc = + op->getParentOfType().lookupSymbol("free"); + if (!freeFunc) { + auto freeType = rewriter.getFunctionType(getVoidPtrType(), {}); + freeFunc = M::FuncOp::create(rewriter.getUnknownLoc(), "free", freeType); + op->getParentOfType().push_back(freeFunc); + } + return freeFunc; + } + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto freemem = M::cast(op); + M::FuncOp freeFunc = genFreeFunc(op, rewriter); + M::Value *casted = rewriter.create( + op->getLoc(), getVoidPtrType(), operands[0]); + rewriter.replaceOpWithNewOp(op, llvm::ArrayRef(), + rewriter.getSymbolRefAttr(freeFunc), casted); + return matchSuccess(); + } +}; + +struct GenDimsOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + // gendims(args:index, ...) ==> %v = ... : [size x <3 x index>] + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto argsize = operands.size(); + assert(argsize % 3 == 0 && "must be a multiple of 3"); + auto loc = op->getLoc(); + auto *ctx = op->getContext(); + auto &llvmCtx = getLLVMContext(); + L::Type *i64 = L::Type::getIntNTy(llvmCtx, 64); + L::Type *v3i64 = L::VectorType::get(i64, 3); + M::Type vec3 = M::LLVM::LLVMType::get(ctx, v3i64); + L::Type *av3i64 = L::ArrayType::get(v3i64, argsize / 3); + M::Type arrvec3 = M::LLVM::LLVMType::get(ctx, av3i64); + auto i32Type = M::LLVM::LLVMType::get(ctx, L::Type::getIntNTy(llvmCtx, 32)); + M::Value *v = rewriter.create( + loc, arrvec3, L::ArrayRef{}); + // BUG? insertelement requires an i32 for 3rd argument + M::Value *zero = rewriter.create( + loc, i32Type, rewriter.getI64IntegerAttr(0)); + M::Value *one = rewriter.create( + loc, i32Type, rewriter.getI64IntegerAttr(1)); + M::Value *two = rewriter.create( + loc, i32Type, rewriter.getI64IntegerAttr(2)); + unsigned rank = 0; + for (std::size_t i = 0; i < argsize;) { + auto a1 = rewriter.create(loc, vec3); + auto a2 = rewriter.create( + loc, vec3, a1, operands[i++], zero); + auto a3 = rewriter.create( + loc, vec3, a2, operands[i++], one); + auto a4 = rewriter.create( + loc, vec3, a3, operands[i++], two); + L::SmallVector attrs; + attrs.push_back(rewriter.getI64IntegerAttr(rank++)); + v = rewriter.create( + loc, arrvec3, v, a4, rewriter.getArrayAttr(attrs)); + } + rewriter.replaceOp(op, v); + return matchSuccess(); + } +}; + +#if 0 +class GlobalExprConversion : public FIROpConversion { +public: + explicit GlobalExprConversion( + M::MLIRContext *ctxt, FIRToLLVMTypeConverter &lowering) + : FIROpConversion(GlobalOp::getOperationName(), 1, ctxt, lowering) {} + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + // FIXME + } +}; +#endif + +struct InsertValueOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto insertVal = cast(op); + // FIXME + assert(false); + return matchSuccess(); + } +}; + +// convert to LLVM IR dialect `load` +struct LoadExprConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto load = M::cast(op); + auto newLoad = rewriter.create(load.getLoc(), + lowering.convertType(load.getType()), operands, load.getAttrs()); + // ???: the next line works around a bug [do we still need this?] + load.replaceAllUsesWith(newLoad.getResult()); + rewriter.replaceOp(op, newLoad.getResult()); + return matchSuccess(); + } +}; + +struct NoReassocOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + // FIXME + assert(false); + return matchSuccess(); + } +}; + +// conversion of fir::SelectOp +struct SelectOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + L::ArrayRef destinations, L::ArrayRef destOperands, + M::ConversionPatternRewriter &rewriter) const override { + // FIXME + assert(false); + return matchSuccess(); + } +}; + +struct SelectCaseOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + L::ArrayRef destinations, L::ArrayRef destOperands, + M::ConversionPatternRewriter &rewriter) const override { + // FIXME + assert(false); + return matchSuccess(); + } +}; + +struct SelectRankOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + L::ArrayRef destinations, L::ArrayRef destOperands, + M::ConversionPatternRewriter &rewriter) const override { + // FIXME + assert(false); + return matchSuccess(); + } +}; + +struct SelectTypeOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + L::ArrayRef destinations, L::ArrayRef destOperands, + M::ConversionPatternRewriter &rewriter) const override { + // FIXME + assert(false); + return matchSuccess(); + } +}; + +// convert to LLVM IR dialect `store` +struct StoreExprConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + rewriter.replaceOpWithNewOp(op, operands[0], operands[1]); + return matchSuccess(); + } +}; + +// convert to LLVM IR dialect `undef` +struct UndefOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto undef = M::cast(op); + M::Value *v{rewriter.create( + undef.getLoc(), lowering.convertType(undef.getType()))}; + rewriter.replaceOp(op, v); + return matchSuccess(); + } +}; + +// convert to LLVM IR dialect `unreachable` +struct UnreachableOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + L::SmallVector destinations; // none + L::SmallVector destOperands; // none + rewriter.create( + op->getLoc(), operands, destinations, destOperands, op->getAttrs()); + return matchSuccess(); + } +}; + +// Lower a SELECT operation into a cascade of conditional branches. The last +// case must be the `true` condition. +inline void rewriteSelectConstruct(M::Operation *op, OperandTy operands, + L::ArrayRef dests, L::ArrayRef destOperands, + M::OpBuilder &rewriter) { + L::SmallVector noargs; + L::SmallVector blocks; + auto loc{op->getLoc()}; + blocks.push_back(rewriter.getInsertionBlock()); + for (std::size_t i = 1; i < dests.size(); ++i) + blocks.push_back(rewriter.createBlock(dests[0])); + rewriter.setInsertionPointToEnd(blocks[0]); + if (dests.size() == 1) { + rewriter.create(loc, dests[0], destOperands[0]); + return; + } + rewriter.create( + loc, operands[1], dests[0], destOperands[0], blocks[1], noargs); + for (std::size_t i = 1; i < dests.size() - 1; ++i) { + rewriter.setInsertionPointToEnd(blocks[i]); + rewriter.create( + loc, operands[i + 1], dests[i], destOperands[i], blocks[i + 1], noargs); + } + std::size_t last{dests.size() - 1}; + rewriter.setInsertionPointToEnd(blocks[last]); + rewriter.create(loc, dests[last], destOperands[last]); +} + +/// Convert FIR dialect to LLVM dialect +/// +/// This pass lowers all FIR dialect operations to LLVM IR dialect. An +/// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. +class FIRToLLVMLoweringPass : public M::ModulePass { + M::OpBuilder *builder; + +public: + void runOnModule() override { + auto &context{getContext()}; + FIRToLLVMTypeConverter typeConverter{&context}; + M::OwningRewritePatternList patterns; + patterns + .insert( + &context, typeConverter); + M::populateStdToLLVMConversionPatterns(typeConverter, patterns); + M::populateFuncOpTypeConversionPattern(patterns, &context, typeConverter); + M::ConversionTarget target{context}; + target.addLegalDialect(); + target.addDynamicallyLegalOp([&](M::FuncOp op) { + return typeConverter.isSignatureLegal(op.getType()); + }); + // required NOP stubs for applying a full conversion + target.addDynamicallyLegalOp( + [&](M::ModuleOp op) { return true; }); + target.addDynamicallyLegalOp( + [&](M::ModuleTerminatorOp op) { return true; }); + + // apply the patterns + if (M::failed(M::applyFullConversion( + getModule(), target, std::move(patterns), &typeConverter))) { + M::emitError(M::UnknownLoc::get(&context), + "error in converting to LLVM-IR dialect\n"); + signalPassFailure(); + } + } +}; + +/// Lower from LLVM IR dialect to proper LLVM-IR and dump the module +struct LLVMIRLoweringPass : public M::ModulePass { + void runOnModule() override { + if (auto llvmModule{M::translateModuleToLLVMIR(getModule())}) { + std::error_code ec; + auto stream{L::raw_fd_ostream("a.ll", ec, L::sys::fs::F_None)}; + stream << *llvmModule << '\n'; + } else { + auto ctxt{getModule().getContext()}; + M::emitError(M::UnknownLoc::get(ctxt), "could not emit LLVM-IR\n"); + signalPassFailure(); + } + } +}; + +} // namespace + +std::unique_ptr fir::createFIRToLLVMPass() { + return std::make_unique(); +} + +// returns the predefined pass +std::unique_ptr fir::createStdToLLVMPass() { + return M::createLowerToLLVMPass(); +} + +std::unique_ptr fir::createLLVMDialectToLLVMPass() { + return std::make_unique(); +} diff --git a/lib/fir/MemToReg.cpp b/lib/fir/MemToReg.cpp new file mode 100644 index 000000000000..4cbbaa21ffc5 --- /dev/null +++ b/lib/fir/MemToReg.cpp @@ -0,0 +1,760 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fir/Transforms/MemToReg.h" +#include "fir/Dialect.h" +#include "fir/FIROps.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "mlir/Analysis/Dominance.h" +#include "mlir/Analysis/IteratedDominanceFrontier.h" +#include "mlir/Dialect/StandardOps/Ops.h" +#include "mlir/Pass/Pass.h" +#include +#include + +namespace M = mlir; + +using namespace fir; + +using DominatorTree = M::DominanceInfo; + +namespace { + +bool isAllocaPromotable(fir::AllocaOp &ae) { + for (auto &use : ae.getResult()->getUses()) { + auto *op = use.getOwner(); + if (auto load = M::dyn_cast(op)) { + // do nothing + } else if (auto stor = M::dyn_cast(op)) { + if (stor.getOperand(0)->getDefiningOp() == op) { + return false; + } + } else { + return false; + } + } + return true; +} + +struct AllocaInfo { + llvm::SmallVector definingBlocks; + llvm::SmallVector usingBlocks; + + M::Operation *onlyStore; + M::Block *onlyBlock; + bool onlyUsedInOneBlock; + + void clear() { + definingBlocks.clear(); + usingBlocks.clear(); + onlyStore = nullptr; + onlyBlock = nullptr; + onlyUsedInOneBlock = true; + } + + /// Scan the uses of the specified alloca, filling in the AllocaInfo used + /// by the rest of the pass to reason about the uses of this alloca. + void analyzeAlloca(fir::AllocaOp &AI) { + clear(); + + // As we scan the uses of the alloca instruction, keep track of stores, + // and decide whether all of the loads and stores to the alloca are within + // the same basic block. + for (auto UI = AI.getResult()->use_begin(), E = AI.getResult()->use_end(); + UI != E;) { + auto *User = UI->getOwner(); + UI++; + + if (auto SI = M::dyn_cast(User)) { + // Remember the basic blocks which define new values for the alloca + definingBlocks.push_back(SI.getOperation()->getBlock()); + onlyStore = SI.getOperation(); + } else { + auto LI = M::cast(User); + // Otherwise it must be a load instruction, keep track of variable + // reads. + usingBlocks.push_back(LI.getOperation()->getBlock()); + } + + if (onlyUsedInOneBlock) { + if (!onlyBlock) + onlyBlock = User->getBlock(); + else if (onlyBlock != User->getBlock()) + onlyUsedInOneBlock = false; + } + } + } +}; + +struct RenamePassData { + using ValVector = std::vector; + + RenamePassData(M::Block *b, M::Block *p, const ValVector &v) + : BB(b), Pred(p), Values(v) {} + RenamePassData(const RenamePassData &) = delete; + RenamePassData &operator=(const RenamePassData &) = delete; + RenamePassData(RenamePassData &&) = default; + ~RenamePassData() = default; + + M::Block *BB; + M::Block *Pred; + ValVector Values; +}; + +struct LargeBlockInfo { + using INMap = llvm::DenseMap; + INMap instNumbers; + + static bool isInterestingInstruction(M::Operation &I) { + if (M::isa(I)) { + if (auto *op = I.getOperand(0)->getDefiningOp()) + return M::isa(op); + } else if (M::isa(I)) { + if (auto *op = I.getOperand(1)->getDefiningOp()) + return M::isa(op); + } + return false; + } + + template unsigned getInstructionIndex(A &op) { + auto *oper = op.getOperation(); + + // has it already been numbered? + INMap::iterator it = instNumbers.find(oper); + if (it != instNumbers.end()) { + return it->second; + } + + // No. search for the oper + auto *block = oper->getBlock(); + unsigned num = 0u; + for (auto &o : block->getOperations()) + if (isInterestingInstruction(o)) { + instNumbers[&o] = num++; + } + it = instNumbers.find(oper); + assert(it != instNumbers.end() && "operation not in block?"); + return it->second; + } + + template void deleteValue(A &op) { + auto *oper = op.getOperation(); + instNumbers.erase(oper); + } + + void clear() { instNumbers.clear(); } +}; + +struct MemToReg : public M::FunctionPass { + explicit MemToReg() {} + + std::vector allocas; + DominatorTree *domTree = nullptr; + M::OpBuilder *builder = nullptr; + + /// Contains a stable numbering of basic blocks to avoid non-determinstic + /// behavior. + llvm::DenseMap BBNumbers; + + /// Reverse mapping of Allocas. + llvm::DenseMap allocaLookup; + + /// The set of basic blocks the renamer has already visited. + llvm::SmallPtrSet Visited; + + llvm::DenseMap, unsigned> BlockArgs; + llvm::DenseMap, unsigned> argToAllocaMap; + + bool rewriteSingleStoreAlloca( + fir::AllocaOp &AI, AllocaInfo &Info, LargeBlockInfo &LBI) { + fir::StoreOp onlyStore(M::cast(Info.onlyStore)); + M::Block *StoreBB = Info.onlyStore->getBlock(); + int StoreIndex = -1; + + // Clear out usingBlocks. We will reconstruct it here if needed. + Info.usingBlocks.clear(); + + for (auto UI = AI.getResult()->use_begin(), E = AI.getResult()->use_end(); + UI != E;) { + auto *UserInst = UI->getOwner(); + UI++; + + if (M::dyn_cast(UserInst)) { + continue; + } + auto LI = M::cast(UserInst); + + // Okay, if we have a load from the alloca, we want to replace it with the + // only value stored to the alloca. We can do this if the value is + // dominated by the store. If not, we use the rest of the MemToReg + // machinery to insert the phi nodes as needed. + if (LI.getOperation()->getBlock() == StoreBB) { + // If we have a use that is in the same block as the store, compare + // the indices of the two instructions to see which one came first. If + // the load came before the store, we can't handle it. + if (StoreIndex == -1) { + StoreIndex = LBI.getInstructionIndex(onlyStore); + } + + if (unsigned(StoreIndex) > LBI.getInstructionIndex(LI)) { + // Can't handle this load, bail out. + Info.usingBlocks.push_back(StoreBB); + continue; + } + } else if (!domTree->dominates(StoreBB, LI.getOperation()->getBlock())) { + // If the load and store are in different blocks, use BB dominance to + // check their relationships. If the store doesn't dom the use, bail + // out. + Info.usingBlocks.push_back(LI.getOperation()->getBlock()); + continue; + } + + // Otherwise, we *can* safely rewrite this load. + M::Value *ReplVal = onlyStore.getOperand(0); + // If the replacement value is the load, this must occur in unreachable + // code. + if (ReplVal == LI.getResult()) { + ReplVal = builder->create(LI.getLoc(), LI.getType()); + } + + LI.replaceAllUsesWith(ReplVal); + LI.erase(); + LBI.deleteValue(LI); + } + + // Finally, after the scan, check to see if the store is all that is left. + if (!Info.usingBlocks.empty()) + return false; // If not, we'll have to fall back for the remainder. + + // Remove the (now dead) store and alloca. + Info.onlyStore->erase(); + + AI.erase(); + return true; + } + + bool promoteSingleBlockAlloca( + fir::AllocaOp &AI, AllocaInfo &Info, LargeBlockInfo &LBI) { + // Walk the use-def list of the alloca, getting the locations of all stores. + using StoresByIndexTy = + llvm::SmallVector, 64>; + StoresByIndexTy StoresByIndex; + + for (auto U = AI.getResult()->use_begin(), E = AI.getResult()->use_end(); + U != E; U++) + if (fir::StoreOp SI = M::dyn_cast(U->getOwner())) { + StoresByIndex.emplace_back(LBI.getInstructionIndex(SI), &SI); + } + + // Sort the stores by their index, making it efficient to do a lookup with a + // binary search. + llvm::sort(StoresByIndex, llvm::less_first()); + + // Walk all of the loads from this alloca, replacing them with the nearest + // store above them, if any. + for (auto UI = AI.getResult()->use_begin(), E = AI.getResult()->use_end(); + UI != E;) { + auto LI = M::dyn_cast(UI->getOwner()); + UI++; + if (!LI) { + continue; + } + + unsigned LoadIdx = LBI.getInstructionIndex(LI); + + // Find the nearest store that has a lower index than this load. + typename StoresByIndexTy::iterator I = llvm::lower_bound(StoresByIndex, + std::make_pair(LoadIdx, static_cast(nullptr)), + llvm::less_first()); + if (I == StoresByIndex.begin()) { + if (StoresByIndex.empty()) { + // If there are no stores, the load takes the undef value. + auto undef = builder->create(LI.getLoc(), LI.getType()); + LI.replaceAllUsesWith(undef.getResult()); + } else { + // There is no store before this load, bail out (load may be affected + // by the following stores - see main comment). + return false; + } + } else { + // Otherwise, there was a store before this load, the load takes its + // value. Note, if the load was marked as nonnull we don't want to lose + // that information when we erase it. So we preserve it with an assume. + M::Value *ReplVal = std::prev(I)->second->getOperand(0); + + // If the replacement value is the load, this must occur in unreachable + // code. + if (ReplVal == LI) { + ReplVal = builder->create(LI.getLoc(), LI.getType()); + } + + LI.replaceAllUsesWith(ReplVal); + } + + LI.erase(); + LBI.deleteValue(LI); + } + + // Remove the (now dead) stores and alloca. + while (!AI.use_empty()) { + auto *ae = AI.getResult(); + for (auto ai = ae->use_begin(), E = ae->use_end(); ai != E; ai++) { + if (fir::StoreOp SI = + M::dyn_cast(ai->get()->getDefiningOp())) { + SI.erase(); + LBI.deleteValue(SI); + } + } + } + + AI.erase(); + return true; + } + + void computeLiveInBlocks(fir::AllocaOp &ae, AllocaInfo &Info, + const llvm::SmallPtrSetImpl &DefBlocks, + llvm::SmallPtrSetImpl &liveInBlks) { + auto *AI = ae.getOperation(); + // To determine liveness, we must iterate through the predecessors of blocks + // where the def is live. Blocks are added to the worklist if we need to + // check their predecessors. Start with all the using blocks. + llvm::SmallVector LiveInBlockWorklist( + Info.usingBlocks.begin(), Info.usingBlocks.end()); + + // If any of the using blocks is also a definition block, check to see if + // the definition occurs before or after the use. If it happens before the + // use, the value isn't really live-in. + for (unsigned i = 0, e = LiveInBlockWorklist.size(); i != e; ++i) { + M::Block *BB = LiveInBlockWorklist[i]; + if (!DefBlocks.count(BB)) { + continue; + } + + // Okay, this is a block that both uses and defines the value. If the + // first reference to the alloca is a def (store), then we know it isn't + // live-in. + for (M::Block::iterator I = BB->begin();; ++I) { + if (auto SI = M::dyn_cast(I)) { + if (SI.getOperand(1)->getDefiningOp() != AI) { + continue; + } + + // We found a store to the alloca before a load. The alloca is not + // actually live-in here. + LiveInBlockWorklist[i] = LiveInBlockWorklist.back(); + LiveInBlockWorklist.pop_back(); + --i; + --e; + break; + } + + if (auto LI = M::dyn_cast(I)) + // Okay, we found a load before a store to the alloca. It is actually + // live into this block. + if (LI.getOperand()->getDefiningOp() == AI) { + break; + } + } + } + + // Now that we have a set of blocks where the phi is live-in, recursively + // add their predecessors until we find the full region the value is live. + while (!LiveInBlockWorklist.empty()) { + M::Block *BB = LiveInBlockWorklist.pop_back_val(); + + // The block really is live in here, insert it into the set. If already + // in the set, then it has already been processed. + if (!liveInBlks.insert(BB).second) { + continue; + } + + // Since the value is live into BB, it is either defined in a predecessor + // or live into it to. Add the preds to the worklist unless they are a + // defining block. + for (M::Block *P : BB->getPredecessors()) { + // The value is not live into a predecessor if it defines the value. + if (DefBlocks.count(P)) { + continue; + } + + // Otherwise it is, add to the worklist. + LiveInBlockWorklist.push_back(P); + } + } + } + + bool addBlockArgument( + M::Block *BB, fir::AllocaOp &Alloca, unsigned allocaNum) { + auto *ae = Alloca.getOperation(); + auto key = std::make_pair(BB, ae); + auto argNoIter = BlockArgs.find(key); + if (argNoIter != BlockArgs.end()) { + return false; + } + auto argNo = BB->getNumArguments(); + BB->addArgument(Alloca.getAllocatedType()); + BlockArgs[key] = argNo; + argToAllocaMap[std::make_pair(BB, argNo)] = allocaNum; + return true; + } + + template + void initOperands(std::vector &opers, M::Location &&loc, + M::Block *dest, unsigned size, unsigned ai, M::Value *val, A &&oldOpers) { + unsigned i = 0; + for (auto v : oldOpers) { + opers[i++] = v; + } + // we must fill additional args with temporary undef values + for (; i < size; ++i) { + if (i == ai) continue; + auto opTy = dest->getArgument(i)->getType(); + auto typedUndef = builder->create(loc, opTy); + opers[i] = typedUndef; + } + opers[ai] = val; + } + + static void eraseIfNoUse(M::Value *val) { + if (val->use_begin() == val->use_end()) { + val->getDefiningOp()->erase(); + } + } + + /// Set the incoming value on the branch side for the `ai`th block argument + void setParam(M::Block *blk, unsigned ai, M::Value *val, M::Block *target, + unsigned size) { + auto *term = blk->getTerminator(); + if (auto br = M::dyn_cast(term)) { + if (br.getNumOperands() <= ai) { + // construct a new BranchOp to replace term + std::vector opers(size); + auto *dest = br.getDest(); + builder->setInsertionPoint(term); + initOperands(opers, br.getLoc(), dest, size, ai, val, br.getOperands()); + builder->create(br.getLoc(), dest, opers); + br.erase(); + } else { + auto *oldParam = br.getOperand(ai); + br.setOperand(ai, val); + eraseIfNoUse(oldParam); + } + } else if (auto cond = M::dyn_cast(term)) { + if (target == cond.getTrueDest()) { + if (cond.getNumTrueOperands() <= ai) { + // construct a new CondBranchOp to replace term + std::vector opers(size); + auto *dest = cond.getTrueDest(); + builder->setInsertionPoint(term); + initOperands(opers, cond.getLoc(), dest, size, ai, val, + cond.getTrueOperands()); + auto *c = cond.getCondition(); + auto *othDest = cond.getFalseDest(); + std::vector othOpers( + cond.false_operand_begin(), cond.false_operand_end()); + builder->create( + cond.getLoc(), c, dest, opers, othDest, othOpers); + cond.erase(); + } else { + auto *oldParam = cond.getTrueOperand(ai); + cond.setTrueOperand(ai, val); + eraseIfNoUse(oldParam); + } + } else { + if (cond.getNumFalseOperands() <= ai) { + // construct a new CondBranchOp to replace term + std::vector opers(size); + auto *dest = cond.getFalseDest(); + builder->setInsertionPoint(term); + initOperands(opers, cond.getLoc(), dest, size, ai, val, + cond.getFalseOperands()); + auto *c = cond.getCondition(); + auto *othDest = cond.getTrueDest(); + std::vector othOpers( + cond.true_operand_begin(), cond.true_operand_end()); + builder->create( + cond.getLoc(), c, othDest, othOpers, dest, opers); + cond.erase(); + } else { + auto *oldParam = cond.getFalseOperand(ai); + cond.setFalseOperand(ai, val); + eraseIfNoUse(oldParam); + } + } + } else { + assert(false && "unhandled terminator"); + } + } + + inline static void addValue(RenamePassData::ValVector &vector, + RenamePassData::ValVector::size_type size, M::Value *value) { + if (vector.size() < size + 1) { + vector.resize(size + 1); + } + vector[size] = value; + } + + /// Recursively traverse the CFG of the function, renaming loads and + /// stores to the allocas which we are promoting. + /// + /// IncomingVals indicates what value each Alloca contains on exit from the + /// predecessor block Pred. + void renamePass(M::Block *BB, M::Block *Pred, + RenamePassData::ValVector &IncomingVals, + std::vector &Worklist) { + NextIteration: + // Does this block take arguments? + if ((!BB->hasNoPredecessors()) && (BB->getNumArguments() > 0)) { + // add the values from block `Pred` to the argument list in the proper + // positions + for (unsigned ai = 0, AI = BB->getNumArguments(); ai != AI; ++ai) { + auto allocaNo = argToAllocaMap[std::make_pair(BB, ai)]; + setParam(Pred, ai, IncomingVals[allocaNo], BB, AI); + // use the block argument, not the live def in the pred block + addValue(IncomingVals, allocaNo, BB->getArgument(ai)); + } + } + + // Don't revisit blocks. + if (!Visited.insert(BB).second) { + return; + } + + M::Block::iterator II = BB->begin(); + while (true) { + if (II == BB->end()) break; + M::Operation &opn = *II; + II++; + + if (auto LI = M::dyn_cast(opn)) { + auto *srcOpn = LI.getOperand()->getDefiningOp(); + if (!srcOpn) { + continue; + } + + auto Src = M::dyn_cast(srcOpn); + if (!Src) { + continue; + } + + llvm::DenseMap::iterator AI = + allocaLookup.find(srcOpn); + if (AI == allocaLookup.end()) { + continue; + } + + M::Value *V = IncomingVals[AI->second]; + + // Anything using the load now uses the current value. + LI.replaceAllUsesWith(V); + LI.erase(); + } else if (auto SI = M::dyn_cast(opn)) { + auto *dstOpn = SI.getOperand(1)->getDefiningOp(); + if (!dstOpn) { + continue; + } + + // Delete this instruction and mark the name as the current holder of + // the value + auto Dest = M::dyn_cast(dstOpn); + if (!Dest) { + continue; + } + + llvm::DenseMap::iterator ai = + allocaLookup.find(dstOpn); + if (ai == allocaLookup.end()) { + continue; + } + + // what value were we writing? + unsigned AllocaNo = ai->second; + addValue(IncomingVals, AllocaNo, SI.getOperand(0)); + SI.erase(); + } + } + + // 'Recurse' to our successors. + auto I = BB->succ_begin(); + auto E = BB->succ_end(); + if (I == E) { + return; + } + + // Keep track of the successors so we don't visit the same successor twice + llvm::SmallPtrSet VisitedSuccs; + + // Handle the first successor without using the worklist. + VisitedSuccs.insert(*I); + Pred = BB; + BB = *I; + ++I; + + for (; I != E; ++I) + if (VisitedSuccs.insert(*I).second) + Worklist.emplace_back(*I, Pred, IncomingVals); + goto NextIteration; + } + + void doPromotion() { + auto F = getFunction(); + std::vector aes; + AllocaInfo info; + LargeBlockInfo lbi; + M::ForwardIDFCalculator IDF(*domTree); + + assert(!allocas.empty()); + + for (unsigned allocaNum = 0, End = allocas.size(); allocaNum != End; + ++allocaNum) { + auto ae = allocas[allocaNum]; + assert(ae.getParentOfType() == F); + if (ae.use_empty()) { + ae.erase(); + continue; + } + info.analyzeAlloca(ae); + builder->setInsertionPointToStart(&F.front()); + if (info.definingBlocks.size() == 1) + if (rewriteSingleStoreAlloca(ae, info, lbi)) { + continue; + } + if (info.onlyUsedInOneBlock) + if (promoteSingleBlockAlloca(ae, info, lbi)) { + continue; + } + + // If we haven't computed a numbering for the BB's in the function, do + // so now. + if (BBNumbers.empty()) { + unsigned ID = 0; + for (auto &BB : F) + BBNumbers[&BB] = ID++; + } + + // Keep the reverse mapping of the 'Allocas' array for the rename pass. + allocaLookup[allocas[allocaNum].getOperation()] = allocaNum; + + // At this point, we're committed to promoting the alloca using IDF's, + // and the standard SSA construction algorithm. Determine which blocks + // need PHI nodes and see if we can optimize out some work by avoiding + // insertion of dead phi nodes. + + // Unique the set of defining blocks for efficient lookup. + llvm::SmallPtrSet DefBlocks( + info.definingBlocks.begin(), info.definingBlocks.end()); + + // Determine which blocks the value is live in. These are blocks which + // lead to uses. + llvm::SmallPtrSet liveInBlks; + computeLiveInBlocks(ae, info, DefBlocks, liveInBlks); + + // At this point, we're committed to promoting the alloca using IDF's, + // and the standard SSA construction algorithm. Determine which blocks + // need phi nodes and see if we can optimize out some work by avoiding + // insertion of dead phi nodes. + IDF.setLiveInBlocks(liveInBlks); + IDF.setDefiningBlocks(DefBlocks); + llvm::SmallVector PHIBlocks; + IDF.calculate(PHIBlocks); + llvm::sort(PHIBlocks, [this](M::Block *A, M::Block *B) { + return BBNumbers.find(A)->second < BBNumbers.find(B)->second; + }); + + for (M::Block *BB : PHIBlocks) + addBlockArgument(BB, ae, allocaNum); + + aes.push_back(ae); + } + std::swap(allocas, aes); + if (allocas.empty()) { + return; + } + + lbi.clear(); + + // Set the incoming values for the basic block to be null values for all + // of the alloca's. We do this in case there is a load of a value that + // has not been stored yet. In this case, it will get this null value. + RenamePassData::ValVector Values(allocas.size()); + for (unsigned i = 0, e = allocas.size(); i != e; ++i) { + Values[i] = builder->create( + allocas[i].getLoc(), allocas[i].getAllocatedType()); + } + + // Walks all basic blocks in the function performing the SSA rename + // algorithm and inserting the phi nodes we marked as necessary + std::vector renameWorklist; + renameWorklist.emplace_back(&F.front(), nullptr, Values); + do { + RenamePassData RPD(std::move(renameWorklist.back())); + renameWorklist.pop_back(); + // renamePass may add new worklist entries. + renamePass(RPD.BB, RPD.Pred, RPD.Values, renameWorklist); + } while (!renameWorklist.empty()); + + // The renamer uses the Visited set to avoid infinite loops. Clear it + // now. + Visited.clear(); + + // Remove the allocas themselves from the function. + for (auto aa : allocas) { + M::Operation *A = aa.getOperation(); + // If there are any uses of the alloca instructions left, they must be + // in unreachable basic blocks that were not processed by walking the + // dominator tree. Just delete the users now. + if (!A->use_empty()) { + auto undef = builder->create(aa.getLoc(), aa.getType()); + aa.replaceAllUsesWith(undef.getResult()); + } + aa.erase(); + } + } + + /// run the MemToReg pass on the FIR dialect + void runOnFunction() override { + auto f = getFunction(); + auto &entry = f.front(); + auto bldr = M::OpBuilder(f.getBody()); + + domTree = &getAnalysis(); + builder = &bldr; + + while (true) { + allocas.clear(); + + for (auto &op : entry) + if (auto ae = M::dyn_cast(op)) { + if (isAllocaPromotable(ae)) { + allocas.push_back(ae); + } + } + + if (allocas.empty()) { + break; + } + doPromotion(); + } + + domTree = nullptr; + builder = nullptr; + } +}; + +} // namespace + +std::unique_ptr fir::createMemToRegPass() { + return std::make_unique(); +} diff --git a/lib/fir/StdConverter.cpp b/lib/fir/StdConverter.cpp new file mode 100644 index 000000000000..630259da3e9c --- /dev/null +++ b/lib/fir/StdConverter.cpp @@ -0,0 +1,175 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fir/Tilikum/StdConverter.h" +#include "fir/Dialect.h" +#include "fir/FIROps.h" +#include "fir/Type.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Config/abi-breaking.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" +#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h" +#include "mlir/Dialect/AffineOps/AffineOps.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/StandardOps/Ops.h" +#include "mlir/IR/StandardTypes.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Target/LLVMIR.h" +#include "mlir/Transforms/DialectConversion.h" +#include "mlir/Transforms/LowerAffine.h" + +// This module performs the conversion of FIR operations to MLIR standard and/or +// LLVM-IR dialects. + +namespace L = llvm; +namespace M = mlir; + +using namespace fir; + +namespace { + +using SmallVecResult = L::SmallVector; +using OperandTy = L::ArrayRef; +using AttributeTy = L::ArrayRef; + +/// FIR to standard type converter +/// This converts a subset of FIR types to standard types +class FIRToStdTypeConverter : public M::TypeConverter { +public: + explicit FIRToStdTypeConverter() {} + + // convert a front-end kind value to either a std dialect type + static M::Type kindToRealType(M::MLIRContext *ctx, KindTy kind) { + switch (kind) { + case 2: return M::FloatType::getF16(ctx); + case 3: return M::FloatType::getBF16(ctx); + case 4: return M::FloatType::getF32(ctx); + case 8: return M::FloatType::getF64(ctx); + } + return fir::RealType::get(ctx, kind); + } + + /// Convert FIR types to LLVM IR dialect types + M::Type convertType(M::Type t) override { + if (auto cplx = t.dyn_cast()) { + return M::ComplexType::get( + kindToRealType(cplx.getContext(), cplx.getFKind())); + } + if (auto integer = t.dyn_cast()) { + return M::IntegerType::get(integer.getSizeInBits(), integer.getContext()); + } + if (auto real = t.dyn_cast()) { + return kindToRealType(real.getContext(), real.getFKind()); + } + return t; + } +}; + +// Lower a SELECT operation into a cascade of conditional branches. The last +// case must be the `true` condition. +inline void rewriteSelectConstruct(M::Operation *op, OperandTy operands, + L::ArrayRef dests, L::ArrayRef destOperands, + M::OpBuilder &rewriter) { + L::SmallVector noargs; + L::SmallVector blocks; + auto loc{op->getLoc()}; + blocks.push_back(rewriter.getInsertionBlock()); + for (std::size_t i = 1; i < dests.size(); ++i) + blocks.push_back(rewriter.createBlock(dests[0])); + rewriter.setInsertionPointToEnd(blocks[0]); + if (dests.size() == 1) { + rewriter.create(loc, dests[0], destOperands[0]); + return; + } + rewriter.create( + loc, operands[1], dests[0], destOperands[0], blocks[1], noargs); + for (std::size_t i = 1; i < dests.size() - 1; ++i) { + rewriter.setInsertionPointToEnd(blocks[i]); + rewriter.create( + loc, operands[i + 1], dests[i], destOperands[i], blocks[i + 1], noargs); + } + std::size_t last{dests.size() - 1}; + rewriter.setInsertionPointToEnd(blocks[last]); + rewriter.create(loc, dests[last], destOperands[last]); +} + +/// Convert FIR dialect to standard dialect +class FIRToStdLoweringPass : public M::ModulePass { + M::OpBuilder *builder; + + void lowerSelect(M::Operation *op) { + if (M::dyn_cast(op) || M::dyn_cast(op) || + M::dyn_cast(op)) { + // build the lists of operands and successors + L::SmallVector operands{ + op->operand_begin(), op->operand_end()}; + L::SmallVector destinations; + destinations.reserve(op->getNumSuccessors()); + L::SmallVector destOperands; + unsigned firstSuccOpd = op->getSuccessorOperandIndex(0); + for (unsigned i = 0, seen = 0, e = op->getNumSuccessors(); i < e; ++i) { + destinations.push_back(op->getSuccessor(i)); + unsigned n = op->getNumSuccessorOperands(i); + destOperands.push_back( + L::makeArrayRef(operands.data() + firstSuccOpd + seen, n)); + seen += n; + } + // do the rewrite + rewriteSelectConstruct(op, + L::makeArrayRef(operands.data(), operands.data() + firstSuccOpd), + destinations, destOperands, *builder); + } + } + +public: + void runOnModule() override { + return; + + for (auto fn : getModule().getOps()) { + M::OpBuilder rewriter{&fn.getBody()}; + builder = &rewriter; + fn.walk([&](M::Operation *op) { lowerSelect(op); }); + } + auto &context{getContext()}; + FIRToStdTypeConverter typeConverter; + M::OwningRewritePatternList patterns; + // patterns.insert<>(&context, typeConverter); + M::populateAffineToStdConversionPatterns(patterns, &context); + M::populateFuncOpTypeConversionPattern(patterns, &context, typeConverter); + M::ConversionTarget target{context}; + target.addLegalDialect(); + target.addDynamicallyLegalOp([&](M::FuncOp op) { + return typeConverter.isSignatureLegal(op.getType()); + }); + target.addDynamicallyLegalOp( + [&](M::ModuleOp op) { return true; }); + if (M::failed(M::applyPartialConversion( + getModule(), target, std::move(patterns), &typeConverter))) { + M::emitError(M::UnknownLoc::get(&context), + "error in converting to standard dialect\n"); + signalPassFailure(); + } + } +}; + +} // namespace + +std::unique_ptr fir::createFIRToStdPass() { + return std::make_unique(); +} diff --git a/lib/fir/Type.cpp b/lib/fir/Type.cpp new file mode 100644 index 000000000000..4122c7e4714e --- /dev/null +++ b/lib/fir/Type.cpp @@ -0,0 +1,1306 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fir/Type.h" +#include "fir/Dialect.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/IR/Dialect.h" +#include "mlir/IR/StandardTypes.h" +#include "mlir/Parser.h" + +namespace L = llvm; +namespace M = mlir; + +using namespace fir; + +namespace { + +// Tokens + +enum class TokenKind { + error, + eof, + leftang, + rightang, + leftparen, + rightparen, + leftbrace, + rightbrace, + leftbracket, + rightbracket, + colon, + comma, + period, + eroteme, + ecphoneme, + star, + arrow, + ident, + string, + intlit, +}; + +struct Token { + TokenKind kind; + L::StringRef text; +}; + +// Lexer + +class Lexer { +public: + Lexer(L::StringRef source) : srcBuff{source}, srcPtr{source.begin()} {} + + // consume and return next token from the input. input is advanced to after + // the token. + Token lexToken(); + + // peek ahead to the next non-whitespace character, leaving it on the input + // stream + char nextChar() { + skipWhitespace(); + if (atEnd()) { + return '\0'; + } + return *srcPtr; + } + + // advance the input stream `count` characters + void advance(unsigned count = 1) { + while (count--) { + if (atEnd()) { + break; + } + ++srcPtr; + } + } + + const char *getMarker() { return srcPtr; } + +private: + void skipWhitespace() { + while (!atEnd()) { + switch (*srcPtr) { + case ' ': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': ++srcPtr; continue; + default: break; + } + break; + } + } + + Token formToken(TokenKind kind, const char *tokStart) { + return Token{kind, L::StringRef(tokStart, srcPtr - tokStart)}; + } + + Token emitError(const char *loc, const L::Twine &message) { + return formToken(TokenKind::error, loc); + } + + bool atEnd() const { return srcPtr == srcBuff.end(); } + + Token lexIdent(const char *tokStart); + Token lexNumber(const char *tokStart); + Token lexString(const char *tokStart); + + L::StringRef srcBuff; + const char *srcPtr; +}; + +Token Lexer::lexToken() { + skipWhitespace(); + if (atEnd()) { + return formToken(TokenKind::eof, ""); + } + + const char *tokStart = srcPtr; + switch (*srcPtr++) { + case '<': return formToken(TokenKind::leftang, tokStart); + case '>': return formToken(TokenKind::rightang, tokStart); + case '{': return formToken(TokenKind::leftbrace, tokStart); + case '}': return formToken(TokenKind::rightbrace, tokStart); + case '[': return formToken(TokenKind::leftbracket, tokStart); + case ']': return formToken(TokenKind::rightbracket, tokStart); + case '(': return formToken(TokenKind::leftparen, tokStart); + case ')': return formToken(TokenKind::rightparen, tokStart); + case ':': return formToken(TokenKind::colon, tokStart); + case ',': return formToken(TokenKind::comma, tokStart); + case '"': return lexString(tokStart + 1); + case '-': + if (*srcPtr == '>') { + srcPtr++; + return formToken(TokenKind::arrow, tokStart); + } + return lexNumber(tokStart); + case '+': return lexNumber(tokStart + 1); + case '!': return formToken(TokenKind::ecphoneme, tokStart); + case '?': return formToken(TokenKind::eroteme, tokStart); + case '*': return formToken(TokenKind::star, tokStart); + case '.': return formToken(TokenKind::period, tokStart); + default: + if (std::isalpha(*tokStart)) { + return lexIdent(tokStart); + } + if (std::isdigit(*tokStart)) { + return lexNumber(tokStart); + } + return emitError(tokStart, "unexpected character"); + } +} + +Token Lexer::lexString(const char *tokStart) { + while (!atEnd() && *srcPtr != '"') { + ++srcPtr; + } + Token token{formToken(TokenKind::string, tokStart)}; + ++srcPtr; + return token; +} + +Token Lexer::lexIdent(const char *tokStart) { + while (!atEnd() && (std::isalnum(*srcPtr) || *srcPtr == '_')) { + ++srcPtr; + } + return formToken(TokenKind::ident, tokStart); +} + +Token Lexer::lexNumber(const char *tokStart) { + while (!atEnd() && std::isdigit(*srcPtr)) { + ++srcPtr; + } + return formToken(TokenKind::intlit, tokStart); +} + +class auto_counter { +public: + explicit auto_counter(int &ref) : counter(ref) { ++counter; } + ~auto_counter() { --counter; } + +private: + auto_counter() = delete; + auto_counter(const auto_counter &) = delete; + int &counter; +}; + +/// A FIROpsDialect instance uses a FIRTypeParser object to parse and +/// instantiate all FIR types from .fir files. +class FIRTypeParser { +public: + FIRTypeParser(FIROpsDialect *dialect, L::StringRef rawData, M::Location loc) + : context{dialect->getContext()}, lexer{rawData}, loc{loc} {} + + M::Type parseType(); + +protected: + inline void emitError(M::Location loc, const L::Twine &msg) { + M::emitError(loc, msg); + } + + bool consumeToken(TokenKind tk, const L::Twine &msg) { + auto token = lexer.lexToken(); + if (token.kind != tk) { + emitError(loc, msg); + return true; // error! + } + return false; + } + + bool consumeChar(char ch, const L::Twine &msg) { + auto lexCh = lexer.nextChar(); + if (lexCh != ch) { + emitError(loc, msg); + return true; // error! + } + lexer.advance(); + return false; + } + + template A parseIntLitSingleton(const char *msg) { + if (consumeToken(TokenKind::leftang, "expected '<' in type")) { + return {}; + } + auto token{lexer.lexToken()}; + if (token.kind != TokenKind::intlit) { + emitError(loc, msg); + return {}; + } + KindTy kind; + if (token.text.getAsInteger(0, kind)) { + emitError(loc, "expected integer constant"); + return {}; + } + if (consumeToken(TokenKind::rightang, "expected '>' in type")) { + return {}; + } + if (checkAtEnd()) return {}; + return A::get(getContext(), kind); + } + + // `<` kind `>` + template A parseKindSingleton() { + return parseIntLitSingleton("expected kind parameter"); + } + + // `<` rank `>` + template A parseRankSingleton() { + return parseIntLitSingleton("expected rank parameter"); + } + + // '<' type '>' + template A parseTypeSingleton() { + if (consumeToken(TokenKind::leftang, "expected '<' in type")) { + return {}; + } + auto ofTy = parseNextType(); + if (!ofTy) { + emitError(loc, "expected type parameter"); + return {}; + } + if (consumeToken(TokenKind::rightang, "expected '>' in type")) { + return {}; + } + if (checkAtEnd()) return {}; + return A::get(ofTy); + } + + const char *advanceFuncType(); + std::pair advanceNonFuncType(const Token &lastTkn); + M::Type parseNextType(); + + bool checkAtEnd() { + if (!recursiveCall) { + auto token = lexer.lexToken(); + if (token.kind != TokenKind::eof) { + emitError(loc, "unexpected extra characters"); + return true; + } + } + return false; + } + + // `box` `<` type `>` + BoxType parseBox() { return parseTypeSingleton(); } + + // `boxchar` `<` kind `>` + BoxCharType parseBoxChar() { return parseKindSingleton(); } + + // `boxproc` `<` return-type `>` + BoxProcType parseBoxProc() { return parseTypeSingleton(); } + + // `char` `<` kind `>` + CharacterType parseCharacter() { return parseKindSingleton(); } + + // `dims` `<` rank `>` + DimsType parseDims() { return parseRankSingleton(); } + + // `field` + FieldType parseField() { + if (checkAtEnd()) return {}; + return FieldType::get(getContext()); + } + + // `logical` `<` kind `>` + LogicalType parseLogical() { return parseKindSingleton(); } + + // `int` `<` kind `>` + IntType parseInteger() { return parseKindSingleton(); } + + // `complex` `<` kind `>` + CplxType parseComplex() { return parseKindSingleton(); } + + // `real` `<` kind `>` + RealType parseReal() { return parseKindSingleton(); } + + // `ref` `<` type `>` + ReferenceType parseReference() { return parseTypeSingleton(); } + + // `ptr` `<` type `>` + PointerType parsePointer() { return parseTypeSingleton(); } + + // `heap` `<` type `>` + HeapType parseHeap() { return parseTypeSingleton(); } + + SequenceType::Shape parseShape(); + SequenceType parseSequence(); + + RecordType::TypeList parseTypeList(); + RecordType::TypeList parseLenParamList(); + RecordType parseDerived(); + + // `tdesc` `<` type `>` + TypeDescType parseTypeDesc() { return parseTypeSingleton(); } + + // `void` + M::Type parseVoid() { + if (checkAtEnd()) return {}; + return M::TupleType::get(getContext()); + } + + M::MLIRContext *getContext() const { return context; } + +private: + M::MLIRContext *context; + Lexer lexer; + M::Location loc; + int recursiveCall{-1}; +}; + +const char *FIRTypeParser::advanceFuncType() { + Token token; + int lparen = 0; + while (true) { + token = lexer.lexToken(); + if (token.kind == TokenKind::leftparen) { + lparen++; + } else if (token.kind == TokenKind::rightparen) { + if (lparen == 0) break; + lparen--; + } + } + token = lexer.lexToken(); + if (token.kind != TokenKind::arrow) { + return lexer.getMarker(); + } + advanceNonFuncType(lexer.lexToken()); + return lexer.getMarker(); +} + +std::pair FIRTypeParser::advanceNonFuncType( + const Token &lastTkn) { + if (lastTkn.kind == TokenKind::ecphoneme) { // `!` + auto token = lexer.lexToken(); + if (token.kind == TokenKind::ident) { // `!` ident + if (token.text == "fir") { + token = lexer.lexToken(); + if (token.kind == TokenKind::period) { // `!fir.` + return {true, parseType()}; + } + } + char nextCh = lexer.nextChar(); + if (nextCh == '.') { // `!` ident `.` + lexer.lexToken(); // `.` + lexer.lexToken(); // ident + nextCh = lexer.nextChar(); + } + if (nextCh == '<') { // `!` ... `<` ... `>` + token = lexer.lexToken(); + int lang = 0; + while (true) { + token = lexer.lexToken(); + if (token.kind == TokenKind::leftang) { + lang++; + } else if (token.kind == TokenKind::rightang) { + if (lang == 0) break; + lang--; + } + } + } + } + } else if (lastTkn.kind == TokenKind::ident) { // ident + char nextCh = lexer.nextChar(); + if (nextCh == '<') { // ident `<` ... `>` + auto token = lexer.lexToken(); + int lang = 0; + while (true) { + token = lexer.lexToken(); + if (token.kind == TokenKind::leftang) { + lang++; + } else if (token.kind == TokenKind::rightang) { + if (lang == 0) break; + lang--; + } + } + } + } else if (lastTkn.kind == TokenKind::leftparen) { // `(` ... `)` + int lparen = 0; + while (true) { + auto token = lexer.lexToken(); + if (token.kind == TokenKind::leftparen) { + lparen++; + } else if (token.kind == TokenKind::rightparen) { + if (lparen == 0) break; + lparen--; + } + } + } else { + // This doesn't really look like a valid type. Let the standard type parser + // deal with it. + } + return {false, {}}; +} + +// If this is a `!fir.x` type then recursively parse it now, otherwise figure +// out its extent and call into the standard type parser. +// FIXME: advance by tracking matching pairs as in Parser.cpp +M::Type FIRTypeParser::parseNextType() { + const char *marker = lexer.getMarker(); + Token token = lexer.lexToken(); + if (token.kind == TokenKind::leftparen) { + auto count = advanceFuncType() - marker; + assert(count > 0); + return M::parseType(L::StringRef(marker, count), getContext()); + } + auto pair = advanceNonFuncType(token); + if (pair.first) { + return pair.second; + } + auto count = lexer.getMarker() - marker; + assert(count > 0); + return M::parseType(L::StringRef(marker, count), getContext()); +} + +// Parses either `*` `:` +// or (int | `?`) (`x` (int | `?`))* `:` +SequenceType::Shape FIRTypeParser::parseShape() { + SequenceType::Bounds bounds; + int extent; + Token token = lexer.lexToken(); + if (token.kind == TokenKind::star) { + token = lexer.lexToken(); + if (token.kind != TokenKind::colon) { + emitError(loc, "expected '*' to be followed by ':'"); + return {}; + } + return SequenceType::Shape(); + } + while (true) { + if (token.kind != TokenKind::eroteme) { + goto shape_spec; + } + bounds.emplace_back(false, 0); + goto check_xchar; + shape_spec: + if (token.kind != TokenKind::intlit) { + emitError(loc, "expected an integer or '?' in shape specification"); + return {}; + } + token.text.getAsInteger(10, extent); + bounds.emplace_back(true, extent); + check_xchar: + token = lexer.lexToken(); + if (token.kind == TokenKind::colon) { + return SequenceType::Shape(bounds); + } + if ((token.kind != TokenKind::ident) || (token.text != std::string("x"))) { + emitError(loc, "expected an 'x' or ':' after integer"); + return {}; + } + token = lexer.lexToken(); + } +} + +// bounds ::= lo extent stride | `?` +// `array` `<` bounds (`,` bounds)* `:` type `>` +SequenceType FIRTypeParser::parseSequence() { + if (consumeToken(TokenKind::leftang, "expected '<' in array type")) { + return {}; + } + auto shape = parseShape(); + M::Type eleTy = parseNextType(); + if (!eleTy) { + emitError(loc, "invalid element type"); + return {}; + } + if (consumeToken(TokenKind::rightang, "expected '>' in array type")) { + return {}; + } + if (checkAtEnd()) { + return {}; + } + return SequenceType::get(shape, eleTy); +} + +// Parses: string `:` type (',' string `:` type)* '}' +RecordType::TypeList FIRTypeParser::parseTypeList() { + RecordType::TypeList result; + while (true) { + auto name{lexer.lexToken()}; + if (name.kind != TokenKind::ident) { + emitError(loc, "expected identifier"); + return {}; + } + if (consumeToken(TokenKind::colon, "expected colon")) { + return {}; + } + auto memTy{parseNextType()}; + result.emplace_back(name.text, memTy); + auto token{lexer.lexToken()}; + if (token.kind == TokenKind::rightbrace) { + return result; + } + if (token.kind != TokenKind::comma) { + emitError(loc, "expected ','"); + return {}; + } + } +} + +// Parses: string `:` int-type (',' string `:` int-type)* ')' +RecordType::TypeList FIRTypeParser::parseLenParamList() { + RecordType::TypeList result; + while (true) { + auto name{lexer.lexToken()}; + if (name.kind != TokenKind::ident) { + emitError(loc, "expected identifier"); + return {}; + } + if (consumeToken(TokenKind::colon, "expected colon")) { + return {}; + } + auto memTy{parseNextType()}; + result.emplace_back(name.text, memTy); + auto token{lexer.lexToken()}; + if (token.kind == TokenKind::rightparen) { + return result; + } + if (token.kind != TokenKind::comma) { + emitError(loc, "expected ','"); + return {}; + } + } +} + +// Fortran derived type +// `type` `<` name +// (`(` id `:` type (`,` id `:` type)* `)`)? +// (`{` id `:` type (`,` id `:` type)* `}`)? '>' +RecordType FIRTypeParser::parseDerived() { + if (consumeToken(TokenKind::leftang, "expected '<' in type type")) { + return {}; + } + auto name{lexer.lexToken()}; + if (name.kind != TokenKind::ident) { + emitError(loc, "expected a identifier as name of derived type"); + return {}; + } + auto token = lexer.lexToken(); + RecordType::TypeList kindList; + RecordType::TypeList typeList; + if (token.kind == TokenKind::leftbrace) { + goto parse_fields; + } else if (token.kind != TokenKind::leftparen) { + // degenerate case? + goto check_close; + } + kindList = parseLenParamList(); + token = lexer.lexToken(); + if (token.kind != TokenKind::leftbrace) { + // no fields? + goto check_close; + } +parse_fields: + typeList = parseTypeList(); + token = lexer.lexToken(); +check_close: + if (token.kind != TokenKind::rightang) { + emitError(loc, "expected '>' in type type"); + return {}; + } + if (checkAtEnd()) { + return {}; + } + return RecordType::get(getContext(), name.text, kindList, typeList); +} + +M::Type FIRTypeParser::parseType() { + auto_counter c{recursiveCall}; + auto token = lexer.lexToken(); + if (token.kind == TokenKind::ident) { + if (token.text == "ref") return parseReference(); + if (token.text == "array") return parseSequence(); + if (token.text == "char") return parseCharacter(); + if (token.text == "logical") return parseLogical(); + if (token.text == "real") return parseReal(); + if (token.text == "type") return parseDerived(); + if (token.text == "box") return parseBox(); + if (token.text == "boxchar") return parseBoxChar(); + if (token.text == "boxproc") return parseBoxProc(); + if (token.text == "ptr") return parsePointer(); + if (token.text == "heap") return parseHeap(); + if (token.text == "dims") return parseDims(); + if (token.text == "tdesc") return parseTypeDesc(); + if (token.text == "field") return parseField(); + if (token.text == "int") return parseInteger(); + if (token.text == "complex") return parseComplex(); + if (token.text == "void") return parseVoid(); + emitError(loc, "not a known fir type"); + return {}; + } + emitError(loc, "invalid token"); + return {}; +} + +// !fir.ptr and !fir.heap where X is !fir.ptr, !fir.heap, or !fir.ref +// is undefined and disallowed. +bool singleIndirectionLevel(M::Type ty) { + return !(ty.dyn_cast() || ty.dyn_cast() || + ty.dyn_cast()); +} + +} // anonymous + +namespace fir::detail { + +// Type storage classes + +/// `CHARACTER` storage +struct CharacterTypeStorage : public M::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static CharacterTypeStorage *construct( + M::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 M::TypeStorage { + using KeyTy = unsigned; + + static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + + bool operator==(const KeyTy &key) const { + return key == static_cast(getRank()); + } + + static DimsTypeStorage *construct( + M::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 M::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &) { return L::hash_combine(0); } + + bool operator==(const KeyTy &) const { return true; } + + static FieldTypeStorage *construct( + M::TypeStorageAllocator &allocator, KindTy) { + auto *storage = allocator.allocate(); + return new (storage) FieldTypeStorage{0}; + } + +private: + FieldTypeStorage() = delete; + explicit FieldTypeStorage(KindTy) {} +}; + +/// `LOGICAL` storage +struct LogicalTypeStorage : public M::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static LogicalTypeStorage *construct( + M::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 M::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static IntTypeStorage *construct( + M::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 M::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static CplxTypeStorage *construct( + M::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 M::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static RealTypeStorage *construct( + M::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 M::TypeStorage { + using KeyTy = M::Type; + + static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getElementType(); } + + static BoxTypeStorage *construct( + M::TypeStorageAllocator &allocator, M::Type eleTy) { + assert(eleTy && "element type is null"); + auto *storage = allocator.allocate(); + return new (storage) BoxTypeStorage{eleTy}; + } + + M::Type getElementType() const { return eleTy; } + +protected: + M::Type eleTy; + +private: + BoxTypeStorage() = delete; + explicit BoxTypeStorage(M::Type eleTy) : eleTy{eleTy} {} +}; + +/// Boxed CHARACTER object type +struct BoxCharTypeStorage : public M::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getFKind(); } + + static BoxCharTypeStorage *construct( + M::TypeStorageAllocator &allocator, KindTy kind) { + auto *storage = allocator.allocate(); + return new (storage) BoxCharTypeStorage{kind}; + } + + KindTy getFKind() const { return kind / 8; } + + // a !fir.boxchar always wraps a !fir.char + CharacterType getElementType(M::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 M::TypeStorage { + using KeyTy = M::Type; + + static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getElementType(); } + + static BoxProcTypeStorage *construct( + M::TypeStorageAllocator &allocator, M::Type eleTy) { + assert(eleTy && "element type is null"); + auto *storage = allocator.allocate(); + return new (storage) BoxProcTypeStorage{eleTy}; + } + + M::Type getElementType() const { return eleTy; } + +protected: + M::Type eleTy; + +private: + BoxProcTypeStorage() = delete; + explicit BoxProcTypeStorage(M::Type eleTy) : eleTy{eleTy} {} +}; + +/// Pointer-like object storage +struct ReferenceTypeStorage : public M::TypeStorage { + using KeyTy = M::Type; + + static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getElementType(); } + + static ReferenceTypeStorage *construct( + M::TypeStorageAllocator &allocator, M::Type eleTy) { + assert(eleTy && "element type is null"); + auto *storage = allocator.allocate(); + return new (storage) ReferenceTypeStorage{eleTy}; + } + + M::Type getElementType() const { return eleTy; } + +protected: + M::Type eleTy; + +private: + ReferenceTypeStorage() = delete; + explicit ReferenceTypeStorage(M::Type eleTy) : eleTy{eleTy} {} +}; + +/// Pointer object storage +struct PointerTypeStorage : public M::TypeStorage { + using KeyTy = M::Type; + + static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getElementType(); } + + static PointerTypeStorage *construct( + M::TypeStorageAllocator &allocator, M::Type eleTy) { + assert(eleTy && "element type is null"); + auto *storage = allocator.allocate(); + return new (storage) PointerTypeStorage{eleTy}; + } + + M::Type getElementType() const { return eleTy; } + +protected: + M::Type eleTy; + +private: + PointerTypeStorage() = delete; + explicit PointerTypeStorage(M::Type eleTy) : eleTy{eleTy} {} +}; + +/// Heap memory reference object storage +struct HeapTypeStorage : public M::TypeStorage { + using KeyTy = M::Type; + + static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getElementType(); } + + static HeapTypeStorage *construct( + M::TypeStorageAllocator &allocator, M::Type eleTy) { + assert(eleTy && "element type is null"); + auto *storage = allocator.allocate(); + return new (storage) HeapTypeStorage{eleTy}; + } + + M::Type getElementType() const { return eleTy; } + +protected: + M::Type eleTy; + +private: + HeapTypeStorage() = delete; + explicit HeapTypeStorage(M::Type eleTy) : eleTy{eleTy} {} +}; + +/// Sequence-like object storage +struct SequenceTypeStorage : public M::TypeStorage { + using KeyTy = std::pair; + + static unsigned hashKey(const KeyTy &key) { + auto shapeHash{hash_value(std::get(key))}; + return L::hash_combine(shapeHash, std::get(key)); + } + + bool operator==(const KeyTy &key) const { + return key == KeyTy{getShape(), getElementType()}; + } + + static SequenceTypeStorage *construct( + M::TypeStorageAllocator &allocator, const KeyTy &key) { + auto *storage = allocator.allocate(); + return new (storage) SequenceTypeStorage{key.first, key.second}; + } + + SequenceType::Shape getShape() const { return shape; } + M::Type getElementType() const { return eleTy; } + +protected: + SequenceType::Shape shape; + M::Type eleTy; + +private: + SequenceTypeStorage() = delete; + explicit SequenceTypeStorage( + const SequenceType::Shape &shape, M::Type eleTy) + : shape{shape}, eleTy{eleTy} {} +}; + +/// Derived type storage +struct RecordTypeStorage : public M::TypeStorage { + using KeyTy = std::tuple, + L::ArrayRef>; + + static unsigned hashKey(const KeyTy &key) { + return L::hash_combine(std::get<0>(key).str()); + } + + bool operator==(const KeyTy &key) const { + return std::get<0>(key) == getName(); + } + + static RecordTypeStorage *construct( + M::TypeStorageAllocator &allocator, const KeyTy &key) { + auto *storage = allocator.allocate(); + auto &name = std::get<0>(key); + auto &lens = std::get<1>(key); + auto &members = std::get<2>(key); + return new (storage) RecordTypeStorage{name, lens, members}; + } + + L::StringRef getName() const { return name; } + + // The KindList must be provided at construction for correct hash-consing + L::ArrayRef getLenParamList() const { return lens; } + + void setTypeList(L::ArrayRef list) { types = list; } + L::ArrayRef getTypeList() const { return types; } + +protected: + std::string name; + std::vector lens; + std::vector types; + +private: + RecordTypeStorage() = delete; + explicit RecordTypeStorage(L::StringRef name, + L::ArrayRef lens, + L::ArrayRef types) + : name{name}, lens{lens}, types{types} {} +}; + +/// Type descriptor type storage +struct TypeDescTypeStorage : public M::TypeStorage { + using KeyTy = M::Type; + + static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + + bool operator==(const KeyTy &key) const { return key == getOfType(); } + + static TypeDescTypeStorage *construct( + M::TypeStorageAllocator &allocator, M::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 + M::Type getOfType() const { return ofTy; } + +protected: + M::Type ofTy; + +private: + TypeDescTypeStorage() = delete; + explicit TypeDescTypeStorage(M::Type ofTy) : ofTy{ofTy} {} +}; + +} // detail + +template bool inbounds(A v, B lb, B ub) { + return v >= lb && v < ub; +} + +bool fir::isa_fir_type(mlir::Type t) { + return inbounds(t.getKind(), M::Type::FIRST_FIR_TYPE, M::Type::LAST_FIR_TYPE); +} + +bool fir::isa_std_type(mlir::Type t) { + return inbounds( + t.getKind(), M::Type::FIRST_STANDARD_TYPE, M::Type::LAST_STANDARD_TYPE); +} + +bool fir::isa_fir_or_std_type(mlir::Type t) { + return isa_fir_type(t) || isa_std_type(t); +} + +// CHARACTER + +CharacterType fir::CharacterType::get(M::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_CHARACTER, kind * 8); +} + +int fir::CharacterType::getSizeInBits() const { return getImpl()->getFKind(); } + +// Dims + +DimsType fir::DimsType::get(M::MLIRContext *ctxt, unsigned rank) { + return Base::get(ctxt, FIR_DIMS, rank); +} + +int fir::DimsType::getRank() const { return getImpl()->getRank(); } + +// Field + +FieldType fir::FieldType::get(M::MLIRContext *ctxt, KindTy) { + return Base::get(ctxt, FIR_FIELD, 0); +} + +// LOGICAL + +LogicalType fir::LogicalType::get(M::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_LOGICAL, kind * 8); +} + +int fir::LogicalType::getSizeInBits() const { return getImpl()->getFKind(); } + +// INTEGER + +IntType fir::IntType::get(M::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_INT, kind * 8); +} + +int fir::IntType::getSizeInBits() const { return getImpl()->getFKind(); } + +// COMPLEX + +CplxType fir::CplxType::get(M::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_COMPLEX, kind * 8); +} + +int fir::CplxType::getSizeInBits() const { return getImpl()->getFKind() * 2; } +KindTy fir::CplxType::getFKind() const { return getImpl()->getFKind() / 8; } + +// REAL + +RealType fir::RealType::get(M::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_REAL, kind * 8); +} + +int fir::RealType::getSizeInBits() const { return getImpl()->getFKind(); } + +// Box + +BoxType fir::BoxType::get(M::Type elementType) { + return Base::get(elementType.getContext(), FIR_BOX, elementType); +} + +M::Type fir::BoxType::getEleTy() const { return getImpl()->getElementType(); } + +// BoxChar + +BoxCharType fir::BoxCharType::get(M::MLIRContext *ctxt, KindTy kind) { + return Base::get(ctxt, FIR_BOXCHAR, kind * 8); +} + +CharacterType fir::BoxCharType::getEleTy() const { + return getImpl()->getElementType(getContext()); +} + +// BoxProc + +BoxProcType fir::BoxProcType::get(M::Type elementType) { + return Base::get(elementType.getContext(), FIR_BOXPROC, elementType); +} + +M::Type fir::BoxProcType::getEleTy() const { + return getImpl()->getElementType(); +} + +// Reference + +ReferenceType fir::ReferenceType::get(M::Type elementType) { + return Base::get(elementType.getContext(), FIR_REFERENCE, elementType); +} + +M::Type fir::ReferenceType::getEleTy() const { + return getImpl()->getElementType(); +} + +// Pointer + +PointerType fir::PointerType::get(M::Type elementType) { + if (!singleIndirectionLevel(elementType)) { + assert(false && "FIXME: invalid element type"); + return {}; + } + return Base::get(elementType.getContext(), FIR_POINTER, elementType); +} + +M::Type fir::PointerType::getEleTy() const { + return getImpl()->getElementType(); +} + +// Heap + +HeapType fir::HeapType::get(M::Type elementType) { + if (!singleIndirectionLevel(elementType)) { + assert(false && "FIXME: invalid element type"); + return {}; + } + return Base::get(elementType.getContext(), FIR_HEAP, elementType); +} + +M::Type fir::HeapType::getEleTy() const { return getImpl()->getElementType(); } + +// Sequence + +SequenceType fir::SequenceType::get(const Shape &shape, M::Type elementType) { + auto *ctxt = elementType.getContext(); + return Base::get(ctxt, FIR_SEQUENCE, shape, elementType); +} + +M::Type fir::SequenceType::getEleTy() const { + return getImpl()->getElementType(); +} + +SequenceType::Shape fir::SequenceType::getShape() const { + return getImpl()->getShape(); +} + +// compare if two shapes are equivalent +bool fir::operator==( + const SequenceType::Shape &sh_1, const SequenceType::Shape &sh_2) { + if (sh_1.known != sh_2.known) { + return false; + } + if (!sh_1.known) { + return true; + } + auto &bnd_1 = sh_1.bounds; + auto &bnd_2 = sh_2.bounds; + if (bnd_1.size() != bnd_2.size()) { + return false; + } + for (std::size_t i = 0, end = bnd_1.size(); i != end; ++i) { + if (bnd_1[i].known != bnd_2[i].known) { + return false; + } + if (bnd_1[i].known && bnd_1[i].bound != bnd_2[i].bound) { + return false; + } + } + return true; +} + +// compute the hash of an Extent +L::hash_code fir::hash_value(const SequenceType::Extent &ext) { + return L::hash_combine(ext.known ? ext.bound : 0); +} + +// compute the hash of a Shape +L::hash_code fir::hash_value(const SequenceType::Shape &sh) { + if (sh.known) { + return L::hash_combine_range(sh.bounds.begin(), sh.bounds.end()); + } + return L::hash_combine(0); +} + +// Derived + +RecordType fir::RecordType::get(M::MLIRContext *ctxt, L::StringRef name, + L::ArrayRef lenPList, L::ArrayRef typeList) { + return Base::get(ctxt, FIR_DERIVED, name, lenPList, typeList); +} + +L::StringRef fir::RecordType::getName() { return getImpl()->getName(); } + +RecordType::TypeList fir::RecordType::getTypeList() { + return getImpl()->getTypeList(); +} + +RecordType::TypeList fir::RecordType::getLenParamList() { + return getImpl()->getLenParamList(); +} + +// Type descriptor + +TypeDescType fir::TypeDescType::get(M::Type ofType) { + assert(!ofType.dyn_cast()); + return Base::get(ofType.getContext(), FIR_TYPEDESC, ofType); +} + +M::Type fir::TypeDescType::getOfTy() const { return getImpl()->getOfType(); } + +// Implementation of the thin interface from dialect to type parser + +M::Type fir::parseFirType( + FIROpsDialect *dialect, L::StringRef rawData, M::Location loc) { + FIRTypeParser parser{dialect, rawData, loc}; + return parser.parseType(); +} diff --git a/test/fir/fir-ops.fir b/test/fir/fir-ops.fir new file mode 100644 index 000000000000..89f9e42f0bd4 --- /dev/null +++ b/test/fir/fir-ops.fir @@ -0,0 +1,81 @@ +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> + +func @dvd1() -> !fir.type +func @dvd2() -> !fir.type +func @dvd3() -> !fir.type +func @dvd4() -> !fir.type + +func @arr1() -> !fir.array<10:f32> +//func @arr2() -> !fir.array<10x10:f32> +func @arr3() -> !fir.array +func @arr4() -> !fir.array<10x?:f32> +func @arr5() -> !fir.array +func @arr6() -> !fir.array<*:f32> + +func @mem1() -> !fir.ref +func @mem2() -> !fir.ptr +func @mem3() -> !fir.heap +func @mem4() -> !fir.ref<() -> ()> + +func @box1() -> !fir.box> +func @box2() -> !fir.boxchar<2> +func @box3() -> !fir.boxproc<(i32, i32) -> i64> + +func @oth1() -> !fir.dims<1> +func @oth2() -> !fir.field +func @oth3() -> !fir.tdesc> + +func @instructions() { + %0 = fir.alloca !fir.array<10:i32> : !fir.ref> + %1 = fir.load %0 : !fir.ref> + %2 = fir.alloca i32 : !fir.ref + %3 = constant 22 : i32 + fir.store %3 to %2 : !fir.ref + %4 = fir.undefined i32 + %5 = fir.allocmem !fir.array<100:f32> : !fir.heap> + %6 = fir.embox %5 : (!fir.heap>) -> !fir.box> + %7 = fir.box_addr %6 : (!fir.box>) -> !fir.ref> + %c0 = constant 0 : index +// %8 = fir.box_dims %6, %c0 : (!fir.box>) -> (index, index, index) +// %8 = fir.call @it1() : () -> !fir.int<4> + %8 = call @it1() : () -> !fir.int<4> + %9 = fir.box_elesize %6 : (!fir.box>) -> i64 + %10 = fir.box_isalloc %6 : (!fir.box>) -> i1 + %11 = fir.box_isarray %6 : (!fir.box>) -> i1 + %12 = fir.box_isptr %6 : (!fir.box>) -> i1 + %13 = fir.box_rank %6 : (!fir.box>) -> i64 + %14 = fir.box_tdesc %6 : (!fir.box>) -> !fir.tdesc> +// %15 = call @box2() : () -> !fir.boxchar<2> + %15 = call @it1() : () -> !fir.int<4> +// %16 = fir.boxchar_len %15 : (!fir.boxchar<2>) -> i32 + %16 = call @it1() : () -> !fir.int<4> + %17 = call @box3() : () -> !fir.boxproc<(i32, i32) -> i64> +// %18 = fir.boxproc_host %17 : (!fir.boxproc<(i32, i32) -> i64>) -> (!fir.ref<(i32, i32) -> i64>, !fir.ref<(i32, f64)>) + %18,%19 = fir.boxproc_host %17 : (!fir.boxproc<(i32, i32) -> i64>) -> (!fir.ref<(i32, i32) -> i64>, !fir.ref) + %c10 = constant 10 : i32 + %20 = fir.coordinate_of %5, %c10 : (!fir.heap>, i32) -> !fir.ref +// %21 = fir.field_index "f" : () -> !fir.field + %21 = fir.undefined !fir.field +// %22 = call @dvd4() : () -> !fir.type + %22 = fir.undefined !fir.type + %23 = fir.extract_value %22, %21 : (!fir.type, !fir.field) -> f32 + %c1 = constant 1 : i32 + %24 = fir.gendims %c1, %c10, %c1 : (i32, i32, i32) -> !fir.dims<1> + %cf1 = constant 1.0 : f32 + %25 = fir.insert_value %22, %cf1, %21 : (!fir.type, f32, !fir.field) -> !fir.type +// %26 = fir.len_param_index "f" : (!fir.field) -> i32 + %26 = fir.undefined i32 +// %27 = fir.call @dvd3() : () -> !fir.type + %27 = fir.undefined !fir.type +// %28 = fir.dispatch "method"(%27) : () -> i32 + %28 = fir.undefined i32 + %29 = fir.convert %26 : (i32) -> i64 + %30 = fir.gentypedesc !fir.type : !fir.tdesc> + %31 = fir.no_reassoc %29 : i64 + fir.freemem %5 : !fir.heap> + return +} diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 41983a7b2e39..ea5985098739 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory(f18) +add_subdirectory(fml) diff --git a/tools/f18/CMakeLists.txt b/tools/f18/CMakeLists.txt index 1a4c97ec03a5..7046f621aba4 100644 --- a/tools/f18/CMakeLists.txt +++ b/tools/f18/CMakeLists.txt @@ -53,16 +53,16 @@ foreach(filename ${MODULES}) else() set(depends ${include}/__fortran_builtins.mod) endif() - add_custom_command(OUTPUT ${include}/${filename}.mod - COMMAND f18 -fparse-only -I${include} - ${PROJECT_SOURCE_DIR}/module/${filename}.f90 - WORKING_DIRECTORY ${include} - DEPENDS f18 ${PROJECT_SOURCE_DIR}/module/${filename}.f90 ${depends} + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/${filename}.mod + COMMAND f18 -fparse-only -fdebug-semantics ${PROJECT_SOURCE_DIR}/tools/flang/module/${filename}.f90 + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include" + DEPENDS f18 ${PROJECT_SOURCE_DIR}/tools/flang/module/${filename}.f90 + ) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/${filename}.f18.mod + COMMAND f18 -fparse-only -fdebug-semantics -module-suffix .f18.mod ${PROJECT_SOURCE_DIR}/tools/flang/module/${filename}.f90 + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include" + DEPENDS f18 ${PROJECT_SOURCE_DIR}/tools/flang/module/${filename}.f90 ) - add_custom_command(OUTPUT ${include}/${filename}.f18.mod - DEPENDS ${include}/${filename}.mod - COMMAND ${CMAKE_COMMAND} -E - copy ${include}/${filename}.mod ${include}/${filename}.f18.mod) list(APPEND MODULE_FILES ${include}/${filename}.mod) list(APPEND MODULE_FILES ${include}/${filename}.f18.mod) install(FILES ${include}/${filename}.mod DESTINATION include) diff --git a/tools/fml/CMakeLists.txt b/tools/fml/CMakeLists.txt new file mode 100644 index 000000000000..73a761c0a161 --- /dev/null +++ b/tools/fml/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L/home/eschweitz/clang-mlir/lib -fno-rtti") + +add_executable(fml + fml.cc +) + +set(LIBS + MLIRAffineOps + MLIRLLVMIR + MLIRLoopOps + MLIRStandardOps +) + +whole_archive_link(fml ${LIBS}) +target_link_libraries(fml + FortranCommon + FortranParser + FortranEvaluate + FortranSemantics + FortranBurnside + ${LIBS} +) + +install(TARGETS fml DESTINATION bin) diff --git a/tools/fml/fml.cc b/tools/fml/fml.cc new file mode 100644 index 000000000000..f6f0fc15eb12 --- /dev/null +++ b/tools/fml/fml.cc @@ -0,0 +1,635 @@ +// Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Temporary Fortran front end driver main program for development scaffolding. + +#include "../../lib/common/default-kinds.h" +#include "../../lib/parser/characters.h" +#include "../../lib/parser/dump-parse-tree.h" +#include "../../lib/parser/flang-features.h" +#include "../../lib/parser/message.h" +#include "../../lib/parser/parse-tree-visitor.h" +#include "../../lib/parser/parse-tree.h" +#include "../../lib/parser/parsing.h" +#include "../../lib/parser/provenance.h" +#include "../../lib/parser/unparse.h" +#include "../../lib/semantics/expression.h" +#include "../../lib/semantics/semantics.h" +#include "../../lib/semantics/unparse-with-symbols.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Module.h" +#include "mlir/Parser.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Transforms/Passes.h" +#include "fir/Dialect.h" +#include "fir/Tilikum/LLVMConverter.h" +#include "fir/Tilikum/StdConverter.h" +#include "fir/Transforms/MemToReg.h" +#include "../../lib/burnside/bridge.h" +#include "../../lib/burnside/canonicalize.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Br = Fortran::burnside; + +static std::list argList(int argc, char *const argv[]) { + std::list result; + for (int j = 0; j < argc; ++j) { + result.emplace_back(argv[j]); + } + return result; +} + +struct MeasurementVisitor { + template bool Pre(const A &) { return true; } + template void Post(const A &) { + ++objects; + bytes += sizeof(A); + } + size_t objects{0}, bytes{0}; +}; + +void MeasureParseTree(const Fortran::parser::Program &program) { + MeasurementVisitor visitor; + Fortran::parser::Walk(program, visitor); + std::cout << "Parse tree comprises " << visitor.objects + << " objects and occupies " << visitor.bytes << " total bytes.\n"; +} + +std::vector filesToDelete; + +void CleanUpAtExit() { + for (const auto &path : filesToDelete) { + if (!path.empty()) { + unlink(path.data()); + } + } +} + +struct DriverOptions { + DriverOptions() {} + bool verbose{false}; // -v + bool compileOnly{false}; // -c + std::string outputPath; // -o path + std::vector searchDirectories{"."s}; // -I dir + std::string moduleDirectory{"."s}; // -module dir + std::string moduleFileSuffix{".mod"}; // -moduleSuffix suff + bool forcedForm{false}; // -Mfixed or -Mfree appeared + bool warnOnNonstandardUsage{false}; // -Mstandard + bool warningsAreErrors{false}; // -Werror + Fortran::parser::Encoding encoding{Fortran::parser::Encoding::UTF_8}; + bool parseOnly{false}; + bool dumpProvenance{false}; + bool dumpCookedChars{false}; + bool dumpUnparse{false}; + bool dumpUnparseWithSymbols{false}; + bool dumpParseTree{false}; + bool dumpSymbols{false}; + bool debugLinearFIR{false}; + bool debugResolveNames{false}; + bool debugSemantics{false}; + bool measureTree{false}; + bool runBackend{true}; + bool dumpHLFIR{true}; + bool dumpFIR{true}; + bool lowerToStd{true}; + bool lowerToLLVM{true}; + bool lowerToLLVMIR{true}; + bool isFirInput{false}; + std::vector pgf90Args; + const char *prefix{nullptr}; +}; + +bool ParentProcess() { + if (fork() == 0) { + return false; // in child process + } + int childStat{0}; + wait(&childStat); + if (!WIFEXITED(childStat) || WEXITSTATUS(childStat) != 0) { + exit(EXIT_FAILURE); + } + return true; +} + +void Exec(std::vector &argv, bool verbose = false) { + if (verbose) { + for (size_t j{0}; j < argv.size(); ++j) { + std::cerr << (j > 0 ? " " : "") << argv[j]; + } + std::cerr << '\n'; + } + argv.push_back(nullptr); + execvp(argv[0], &argv[0]); + std::cerr << "execvp(" << argv[0] << ") failed: " << std::strerror(errno) + << '\n'; + exit(EXIT_FAILURE); +} + +void RunOtherCompiler(DriverOptions &driver, char *source, char *relo) { + std::vector argv; + for (size_t j{0}; j < driver.pgf90Args.size(); ++j) { + argv.push_back(driver.pgf90Args[j].data()); + } + char dashC[3] = "-c", dashO[3] = "-o"; + argv.push_back(dashC); + argv.push_back(dashO); + argv.push_back(relo); + argv.push_back(source); + Exec(argv, driver.verbose); +} + +std::string RelocatableName(const DriverOptions &driver, std::string path) { + if (driver.compileOnly && !driver.outputPath.empty()) { + return driver.outputPath; + } + std::string base{path}; + auto slash{base.rfind("/")}; + if (slash != std::string::npos) { + base = base.substr(slash + 1); + } + std::string relo{base}; + auto dot{base.rfind(".")}; + if (dot != std::string::npos) { + relo = base.substr(0, dot); + } + relo += ".o"; + return relo; +} + +int exitStatus{EXIT_SUCCESS}; + +std::string CompileFortran(std::string path, Fortran::parser::Options options, + DriverOptions &driver, + Fortran::semantics::SemanticsContext &semanticsContext) { + if (!driver.forcedForm) { + auto dot{path.rfind(".")}; + if (dot != std::string::npos) { + std::string suffix{path.substr(dot + 1)}; + options.isFixedForm = suffix == "f" || suffix == "F" || suffix == "ff"; + } + } + options.searchDirectories = driver.searchDirectories; + Fortran::parser::Parsing parsing{semanticsContext.allSources()}; + parsing.Prescan(path, options); + if (!parsing.messages().empty() && + (driver.warningsAreErrors || parsing.messages().AnyFatalError())) { + std::cerr << driver.prefix << "could not scan " << path << '\n'; + parsing.messages().Emit(std::cerr, parsing.cooked()); + exitStatus = EXIT_FAILURE; + return {}; + } + if (driver.dumpProvenance) { + parsing.DumpProvenance(std::cout); + return {}; + } + if (driver.dumpCookedChars) { + parsing.messages().Emit(std::cerr, parsing.cooked()); + parsing.DumpCookedChars(std::cout); + return {}; + } + parsing.Parse(&std::cout); + if (options.instrumentedParse) { + parsing.DumpParsingLog(std::cout); + return {}; + } + parsing.ClearLog(); + parsing.messages().Emit(std::cerr, parsing.cooked()); + if (!parsing.consumedWholeFile()) { + parsing.EmitMessage( + std::cerr, parsing.finalRestingPlace(), "parser FAIL (final position)"); + exitStatus = EXIT_FAILURE; + return {}; + } + if ((!parsing.messages().empty() && + (driver.warningsAreErrors || parsing.messages().AnyFatalError())) || + !parsing.parseTree().has_value()) { + std::cerr << driver.prefix << "could not parse " << path << '\n'; + exitStatus = EXIT_FAILURE; + return {}; + } + auto &parseTree{*parsing.parseTree()}; + if (driver.measureTree) { + MeasureParseTree(parseTree); + } + // TODO: Change this predicate to just "if (!driver.debugNoSemantics)" + if (driver.debugSemantics || driver.debugResolveNames || driver.dumpSymbols || + driver.dumpUnparseWithSymbols || driver.debugLinearFIR || + driver.runBackend) { + Fortran::semantics::Semantics semantics{ + semanticsContext, parseTree, parsing.cooked()}; + semantics.Perform(); + semantics.EmitMessages(std::cerr); + if (driver.dumpSymbols) { + semantics.DumpSymbols(std::cout); + } + if (semantics.AnyFatalError()) { + std::cerr << driver.prefix << "semantic errors in " << path << '\n'; + exitStatus = EXIT_FAILURE; + if (driver.dumpParseTree) { + Fortran::parser::DumpTree(std::cout, parseTree); + } + return {}; + } + if (driver.dumpUnparseWithSymbols) { + Fortran::semantics::UnparseWithSymbols( + std::cout, parseTree, driver.encoding); + return {}; + } + } + + // MLIR+FIR + Br::instantiateBurnsideBridge(semanticsContext.defaultKinds()); + Br::crossBurnsideBridge(Br::getBridge(), parseTree); + mlir::ModuleOp mlirModule{Br::getBridge().getModule()}; + mlir::PassManager pm{mlirModule.getContext()}; + if (driver.dumpHLFIR) { + llvm::outs() << ";== 1 ==\n"; + mlirModule.dump(); + } + pm.addPass(fir::createMemToRegPass()); + // Run FIR lowering and CSE as a pair + pm.addPass(mlir::createCSEPass()); + if (driver.lowerToStd) { + pm.addPass(fir::createFIRToStdPass()); + } + if (driver.lowerToLLVM) { + pm.addPass(fir::createFIRToLLVMPass()); + } + if (driver.lowerToLLVMIR) { + pm.addPass(fir::createLLVMDialectToLLVMPass()); + } + auto result{pm.run(mlirModule)}; + if (driver.dumpFIR) { + llvm::outs() << ";== 2 ==\n"; + mlirModule.dump(); + } + return {}; +} + +// For handling .fir files (MLIR+FIR) +std::string CompileFir(std::string path, Fortran::parser::Options options, + DriverOptions &driver, + Fortran::semantics::SemanticsContext &semanticsContext) { + Br::instantiateBurnsideBridge(semanticsContext.defaultKinds()); + + // check that there is a file to load + llvm::ErrorOr> fileOrErr = + llvm::MemoryBuffer::getFileOrSTDIN(path); + if (std::error_code EC = fileOrErr.getError()) { + llvm::errs() << "Could not open file: " << EC.message() << '\n'; + std::exit(-1); + } + + // load the file into a module + llvm::SourceMgr sourceMgr; + sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), llvm::SMLoc()); + Br::getBridge().parseSourceFile(sourceMgr); + auto mlirModule = Br::getBridge().getModule(); + if (!Br::getBridge().validModule()) { + llvm::errs() << "Error can't load file " << path << '\n'; + std::exit(3); + } + if (mlir::failed(mlirModule.verify())) { + llvm::errs() << "Error verifying FIR module\n"; + std::exit(4); + } + + llvm::outs() << ";== 3 ==\n"; + mlirModule.dump(); + + // run passes + mlir::PassManager pm{mlirModule.getContext()}; + pm.addPass(fir::createMemToRegPass()); + pm.addPass(mlir::createCSEPass()); + if (driver.lowerToStd) { + pm.addPass(fir::createFIRToStdPass()); + } + if (driver.lowerToLLVM) { + pm.addPass(fir::createFIRToLLVMPass()); + } + if (driver.lowerToLLVMIR) { + pm.addPass(fir::createLLVMDialectToLLVMPass()); + } + auto result{pm.run(mlirModule)}; + llvm::outs() << ";== 4 ==\n"; + mlirModule.dump(); + if (mlir::succeeded(result)) { + llvm::outs() << "a.ll written\n"; + } else { + llvm::errs() << "FAILED\n"; + std::exit(5); + } + return {}; +} + +std::string CompileOtherLanguage(std::string path, DriverOptions &driver) { + std::string relo{RelocatableName(driver, path)}; + if (ParentProcess()) { + if (!driver.compileOnly && driver.outputPath.empty()) { + filesToDelete.push_back(relo); + } + return relo; + } + RunOtherCompiler(driver, path.data(), relo.data()); + return {}; +} + +void Link(std::vector &relocatables, DriverOptions &driver) { + if (!ParentProcess()) { + std::vector argv; + for (size_t j{0}; j < driver.pgf90Args.size(); ++j) { + argv.push_back(driver.pgf90Args[j].data()); + } + for (auto &relo : relocatables) { + argv.push_back(relo.data()); + } + if (!driver.outputPath.empty()) { + char dashO[3] = "-o"; + argv.push_back(dashO); + argv.push_back(driver.outputPath.data()); + } + Exec(argv, driver.verbose); + } +} + +int main(int argc, char *const argv[]) { + + atexit(CleanUpAtExit); + + DriverOptions driver; + const char *pgf90{getenv("F18_FC")}; + driver.pgf90Args.push_back(pgf90 ? pgf90 : "pgf90"); + + std::list args{argList(argc, argv)}; + std::string prefix{args.front()}; + args.pop_front(); + prefix += ": "; + driver.prefix = prefix.data(); + + Fortran::parser::Options options; + options.predefinitions.emplace_back("__F18", "1"); + options.predefinitions.emplace_back("__F18_MAJOR__", "1"); + options.predefinitions.emplace_back("__F18_MINOR__", "1"); + options.predefinitions.emplace_back("__F18_PATCHLEVEL__", "1"); +#if __x86_64__ + options.predefinitions.emplace_back("__x86_64__", "1"); +#endif + + Fortran::common::IntrinsicTypeDefaultKinds defaultKinds; + + std::vector fortranSources, firSources, otherSources, + relocatables; + bool anyFiles{false}; + + while (!args.empty()) { + std::string arg{std::move(args.front())}; + args.pop_front(); + if (arg.empty()) { + } else if (arg.at(0) != '-') { + anyFiles = true; + auto dot{arg.rfind(".")}; + if (dot == std::string::npos) { + driver.pgf90Args.push_back(arg); + } else { + std::string suffix{arg.substr(dot + 1)}; + if (suffix == "f" || suffix == "F" || suffix == "ff" || + suffix == "f90" || suffix == "F90" || suffix == "ff90" || + suffix == "f95" || suffix == "F95" || suffix == "ff95" || + suffix == "cuf" || suffix == "CUF" || suffix == "f18" || + suffix == "F18" || suffix == "ff18") { + fortranSources.push_back(arg); + } else if (suffix == "fir" || suffix == "ir" || suffix == "mlir") { + firSources.push_back(arg); + } else if (suffix == "o" || suffix == "a") { + relocatables.push_back(arg); + } else { + otherSources.push_back(arg); + } + } + } else if (arg == "-") { + fortranSources.push_back("-"); + } else if (arg == "--") { + while (!args.empty()) { + fortranSources.emplace_back(std::move(args.front())); + args.pop_front(); + } + break; + } else if (arg == "-Mfixed") { + driver.forcedForm = true; + options.isFixedForm = true; + } else if (arg == "-Mfree") { + driver.forcedForm = true; + options.isFixedForm = false; + } else if (arg == "-Mextend") { + options.fixedFormColumns = 132; + } else if (arg == "-Mbackslash") { + options.features.Enable( + Fortran::parser::LanguageFeature::BackslashEscapes, false); + } else if (arg == "-Mnobackslash") { + options.features.Enable( + Fortran::parser::LanguageFeature::BackslashEscapes); + } else if (arg == "-Mstandard") { + driver.warnOnNonstandardUsage = true; + } else if (arg == "-fopenmp") { + options.features.Enable(Fortran::parser::LanguageFeature::OpenMP); + options.predefinitions.emplace_back("_OPENMP", "201511"); + } else if (arg == "-Werror") { + driver.warningsAreErrors = true; + } else if (arg == "-ed") { + options.features.Enable(Fortran::parser::LanguageFeature::OldDebugLines); + } else if (arg == "-E") { + driver.dumpCookedChars = true; + } else if (arg == "-fbackslash") { + options.features.Enable( + Fortran::parser::LanguageFeature::BackslashEscapes); + } else if (arg == "-fno-backslash") { + options.features.Enable( + Fortran::parser::LanguageFeature::BackslashEscapes, false); + } else if (arg == "-fdebug-dump-provenance") { + driver.dumpProvenance = true; + } else if (arg == "-fdebug-dump-parse-tree") { + driver.dumpParseTree = true; + } else if (arg == "-fdebug-dump-symbols") { + driver.dumpSymbols = true; + } else if (arg == "-fdebug-resolve-names") { + driver.debugResolveNames = true; + } else if (arg == "-fdebug-measure-parse-tree") { + driver.measureTree = true; + } else if (arg == "-fdebug-instrumented-parse") { + options.instrumentedParse = true; + } else if (arg == "-fdebug-semantics") { + // TODO: Enable by default once basic tests pass + driver.debugSemantics = true; + } else if (arg == "-funparse") { + driver.dumpUnparse = true; + } else if (arg == "-funparse-with-symbols") { + driver.dumpUnparseWithSymbols = true; + } else if (arg == "-fdebug-dump-linear-ir") { + driver.debugLinearFIR = true; + } else if (arg == "-fno-dump-hl-fir") { + driver.dumpHLFIR = false; + } else if (arg == "-fno-dump-fir") { + driver.dumpFIR = false; + } else if (arg == "-fno-lower-to-llvm") { + driver.lowerToLLVMIR = false; + } else if (arg == "-fno-lower-to-llvm-dialect") { + driver.lowerToLLVM = false; + driver.lowerToLLVMIR = false; + } else if (arg == "-fno-lower-to-std") { + driver.lowerToStd = false; + driver.lowerToLLVM = false; + driver.lowerToLLVMIR = false; + } else if (arg == "-fparse-only") { + driver.parseOnly = true; + } else if (arg == "-c") { + driver.compileOnly = true; + } else if (arg == "-o") { + driver.outputPath = args.front(); + args.pop_front(); + } else if (arg.substr(0, 2) == "-D") { + auto eq{arg.find('=')}; + if (eq == std::string::npos) { + options.predefinitions.emplace_back(arg.substr(2), "1"); + } else { + options.predefinitions.emplace_back( + arg.substr(2, eq - 2), arg.substr(eq + 1)); + } + } else if (arg.substr(0, 2) == "-U") { + options.predefinitions.emplace_back( + arg.substr(2), std::optional{}); + } else if (arg == "-r8" || arg == "-fdefault-real-8") { + defaultKinds.set_defaultRealKind(8); + } else if (arg == "-i8" || arg == "-fdefault-integer-8") { + defaultKinds.set_defaultIntegerKind(8); + } else if (arg == "-module") { + driver.moduleDirectory = args.front(); + args.pop_front(); + } else if (arg == "-module-suffix") { + driver.moduleFileSuffix = args.front(); + args.pop_front(); + } else if (arg == "-help" || arg == "--help" || arg == "-?") { + std::cerr + << "f18 options:\n" + << " -Mfixed | -Mfree force the source form\n" + << " -Mextend 132-column fixed form\n" + << " -f[no-]backslash enable[disable] \\escapes in literals\n" + << " -M[no]backslash disable[enable] \\escapes in literals\n" + << " -Mstandard enable conformance warnings\n" + << " -r8 | -fdefault-real-8 | -i8 | -fdefault-integer-8 " + "change default kinds of intrinsic types\n" + << " -Werror treat warnings as errors\n" + << " -ed enable fixed form D lines\n" + << " -E prescan & preprocess only\n" + << " -module dir module output directory (default .)\n" + << " -fparse-only parse only, no output except messages\n" + << " -funparse parse & reformat only, no code " + "generation\n" + << " -funparse-with-symbols parse, resolve symbols, and unparse\n" + << " -fdebug-measure-parse-tree\n" + << " -fdebug-dump-provenance\n" + << " -fdebug-dump-parse-tree\n" + << " -fdebug-dump-symbols\n" + << " -fdebug-resolve-names\n" + << " -fdebug-instrumented-parse\n" + << " -fdebug-semantics perform semantic checks\n" + << " -fdotty print FIR as a dotty graph\n" + << " -fdebug-dump-linear-ir dump the flat linear FIR for debug\n" + << " -v -c -o -I -D -U have their usual meanings\n" + << " -help print this again\n" + << "Other options are passed through to the compiler.\n"; + return exitStatus; + } else if (arg == "-V") { + std::cerr << "\nf18 compiler (under development)\n"; + return exitStatus; + } else { + driver.pgf90Args.push_back(arg); + if (arg == "-v") { + driver.verbose = true; + } else if (arg == "-I") { + driver.pgf90Args.push_back(args.front()); + driver.searchDirectories.push_back(args.front()); + args.pop_front(); + } else if (arg.substr(0, 2) == "-I") { + driver.searchDirectories.push_back(arg.substr(2)); + } + } + } + + if (driver.warnOnNonstandardUsage) { + options.features.WarnOnAllNonstandard(); + } + if (!options.features.IsEnabled( + Fortran::parser::LanguageFeature::BackslashEscapes)) { + driver.pgf90Args.push_back("-Mbackslash"); + } + if (options.features.IsEnabled(Fortran::parser::LanguageFeature::OpenMP)) { + driver.pgf90Args.push_back("-mp"); + } + + Fortran::parser::AllSources allSources; + Fortran::semantics::SemanticsContext semanticsContext{ + defaultKinds, options.features, allSources}; + semanticsContext.set_moduleDirectory(driver.moduleDirectory) + .set_moduleFileSuffix(driver.moduleFileSuffix) + .set_searchDirectories(driver.searchDirectories) + .set_warnOnNonstandardUsage(driver.warnOnNonstandardUsage) + .set_warningsAreErrors(driver.warningsAreErrors); + + if (!anyFiles) { + driver.measureTree = true; + driver.dumpUnparse = true; + CompileFortran("-", options, driver, semanticsContext); + return exitStatus; + } + for (const auto &path : fortranSources) { + std::string relo{CompileFortran(path, options, driver, semanticsContext)}; + if (!driver.compileOnly && !relo.empty()) { + relocatables.push_back(relo); + } + } + for (const auto &path : firSources) { + std::string relo{CompileFir(path, options, driver, semanticsContext)}; + if (!driver.compileOnly && !relo.empty()) { + relocatables.push_back(relo); + } + } + for (const auto &path : otherSources) { + std::string relo{CompileOtherLanguage(path, driver)}; + if (!driver.compileOnly && !relo.empty()) { + relocatables.push_back(relo); + } + } + if (!relocatables.empty()) { + Link(relocatables, driver); + } + return exitStatus; +} From 999466cf0cf681f5d90ff85118a2e50e2349ad8e Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 11 Sep 2019 22:10:08 -0700 Subject: [PATCH 003/123] play a little where's waldo --- lib/burnside/canonicalize.h | 2 +- lib/decimal/binary-to-decimal.cc | 9 +++++++++ lib/decimal/decimal-to-binary.cc | 10 ++++++++++ test/fir/fir-ops.fir | 2 ++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/burnside/canonicalize.h b/lib/burnside/canonicalize.h index 59d441b6fc94..e8de6c068b08 100644 --- a/lib/burnside/canonicalize.h +++ b/lib/burnside/canonicalize.h @@ -32,7 +32,7 @@ class AllocaExpr; namespace Fortran { namespace evaluate { template class Expr; -class SomeType; +struct SomeType; } // evaluate namespace semantics { class Symbol; diff --git a/lib/decimal/binary-to-decimal.cc b/lib/decimal/binary-to-decimal.cc index 50bca7327640..c4157a5cc533 100644 --- a/lib/decimal/binary-to-decimal.cc +++ b/lib/decimal/binary-to-decimal.cc @@ -312,6 +312,15 @@ void BigRadixFloatingPointNumber::LoseLeastSignificantDigit() +INSTANTIATE_WALDO(8); +INSTANTIATE_WALDO(11); +INSTANTIATE_WALDO(24); +INSTANTIATE_WALDO(53); +INSTANTIATE_WALDO(64); +INSTANTIATE_WALDO(112); + template ConversionToDecimalResult ConvertToDecimal(char *buffer, size_t size, enum DecimalConversionFlags flags, int digits, diff --git a/lib/decimal/decimal-to-binary.cc b/lib/decimal/decimal-to-binary.cc index 34ecb7cc664e..5559ab30931b 100644 --- a/lib/decimal/decimal-to-binary.cc +++ b/lib/decimal/decimal-to-binary.cc @@ -402,6 +402,16 @@ template ConversionToBinaryResult<64> ConvertToBinary<64>( template ConversionToBinaryResult<112> ConvertToBinary<112>( const char *&, enum FortranRounding); +#define INSTANTIATE_WALDO(X) \ + template ConversionToBinaryResult \ + BigRadixFloatingPointNumber::ConvertToBinary() +INSTANTIATE_WALDO(8); +INSTANTIATE_WALDO(11); +INSTANTIATE_WALDO(24); +INSTANTIATE_WALDO(53); +INSTANTIATE_WALDO(64); +INSTANTIATE_WALDO(112); + extern "C" { enum ConversionResultFlags ConvertDecimalToFloat( const char **p, float *f, enum FortranRounding rounding) { diff --git a/test/fir/fir-ops.fir b/test/fir/fir-ops.fir index 89f9e42f0bd4..fd87c524e41a 100644 --- a/test/fir/fir-ops.fir +++ b/test/fir/fir-ops.fir @@ -1,3 +1,5 @@ +// Test the FIR operations + func @it1() -> !fir.int<4> func @it2() -> !fir.real<8> func @it3() -> !fir.complex<8> From 1616833ff3f0c13428f599953b9331a556bb750d Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 12 Sep 2019 12:53:12 -0700 Subject: [PATCH 004/123] finish the pretty printer/parser add "How To Lower" doc changes to put optional attributes in and position them before the type allow unquoted identifiers turn off warnings on Mac --- documentation/BurnsideToFIR.md | 823 ++++++++++++++++++ documentation/FIRLangRef.md | 228 ++--- include/fir/.clang-format | 2 + include/fir/CMakeLists.txt | 3 - include/fir/FIROps.td | 363 ++++---- include/fir/Tilikum/CMakeLists.txt | 14 - include/fir/Tilikum/LLVMConverter.h | 4 +- include/fir/Tilikum/StdConverter.h | 4 +- include/fir/Transforms/CMakeLists.txt | 14 - include/fir/Type.h | 63 +- lib/CMakeLists.txt | 6 + lib/burnside/CMakeLists.txt | 2 +- lib/burnside/bridge.cc | 441 +++++----- lib/burnside/bridge.h | 38 +- lib/burnside/builder.cc | 32 +- lib/burnside/common.h | 62 -- .../{canonicalize.cc => convert-expr.cc} | 9 +- .../{canonicalize.h => convert-expr.h} | 9 +- lib/burnside/expression.cc | 405 --------- lib/burnside/expression.h | 95 -- lib/burnside/fe-helper.cc | 22 +- lib/burnside/fe-helper.h | 15 +- lib/burnside/flattened.cc | 178 ++-- lib/burnside/flattened.h | 79 +- lib/burnside/mixin.h | 2 + lib/burnside/runtime.h | 3 +- lib/fir/.clang-format | 2 + lib/fir/Attribute.cpp | 25 +- lib/fir/CMakeLists.txt | 1 + lib/fir/Dialect.cpp | 44 +- lib/fir/FIROps.cpp | 267 +++--- lib/fir/IteratedDominanceFrontier.cpp | 107 +++ lib/fir/LLVMConverter.cpp | 254 +++--- lib/fir/MemToReg.cpp | 87 +- lib/fir/StdConverter.cpp | 51 +- lib/fir/Type.cpp | 641 +++++++++----- test/fir/fir-ops.fir | 204 ++++- test/fir/fir-types.fir | 38 + tools/fml/CMakeLists.txt | 2 +- tools/fml/fml.cc | 27 +- 40 files changed, 2721 insertions(+), 1945 deletions(-) create mode 100644 documentation/BurnsideToFIR.md create mode 100644 include/fir/.clang-format delete mode 100644 include/fir/Tilikum/CMakeLists.txt delete mode 100644 include/fir/Transforms/CMakeLists.txt delete mode 100644 lib/burnside/common.h rename lib/burnside/{canonicalize.cc => convert-expr.cc} (99%) rename lib/burnside/{canonicalize.h => convert-expr.h} (86%) delete mode 100644 lib/burnside/expression.cc delete mode 100644 lib/burnside/expression.h create mode 100644 lib/fir/.clang-format create mode 100644 lib/fir/IteratedDominanceFrontier.cpp create mode 100644 test/fir/fir-types.fir diff --git a/documentation/BurnsideToFIR.md b/documentation/BurnsideToFIR.md new file mode 100644 index 000000000000..de4d1ff44dd9 --- /dev/null +++ b/documentation/BurnsideToFIR.md @@ -0,0 +1,823 @@ +## Burnside: The Bridge from the Fortran front-end to FIR + +This document sketches the translation of various Fortran snippets from +their syntactic level to how they ought to be represented in FIR. These +translations are representative and written in pseudo-code. + +This document shows examples of how Fortran fragments might be lowered into +FIR fragments. The style used throughout the document is to first show the +Fortran code fragment above a line and the FIR code fragment below the +line. + +### Program Units (PROGRAM, MODULE, and SUBMODULE) + +FIR has one flat global namespace. The global namespace can be populated +by Ops that represent code (functions), data (variables, constants), and +auxiliary structures (dispatch tables). + +Name collisions and scoping will be handled by a name mangling scheme. This +scheme ought to be a bijection from the tree of Fortran syntactic symbols +to and from the set of mangled names. + +A `PROGRAM` will necessarily have its executable definition wrapped in a +FIR `func` like a `SUBROUTINE`. Again, it is assumed the name mangling +scheme will provide a mapping to a distinct name. + +### Procedures (FUNCTION and SUBROUTINE) + +```fortran + FUNCTION foo (arg1, arg2) RESULT retval + + SUBROUTINE bar (arg1, arg2) +``` +---- +```mlir + func @foo(!fir.ref, !fir.ref) -> !TR + func @bar(!fir.ref, !fir.ref) +``` + +MLIR is strongly typed, so the types of the arguments and return value(s), +if any, must be explicitly specified. (Here, `arg1`, `arg2`, and `retval` +have the types `!T1`, `!T2`, and `!TR`, resp.) Also reflected is the +default calling convention: Fortran passes arguments by reference. + +#### Internal subprograms + +These will be lowered as any other `SUBROUTINE`. The difference will be +that they may take an extra `tuple` reference argument to refer to +variables in the host context. Host associated variables must be bundled +and passed explicitly on the FIR side. An example will be detailed below. + +#### Statement functions + +These are very simple internal subroutines, in a sense. They will be +lowered in the same way. + +### Non-executable statements + +#### Data + +Some non-executable statements may create constant (`PARAMETER`) or +variable data. This information should be lowered. + +##### Constants + +```fortran + INTEGER, PARAMETER :: x = 1 + CHARACTER (LEN = 10), PARAMETER :: DIGITS = "0123456789" +``` +---- +```mlir + %0 = constant 1 : i32 + + fir.global @_QG_digits constant : !fir.array<10:!fir.char<1>> { + constant '0' : !fir.char<1> + ... + constant '9' : !fir.char<1> + } +``` + +##### Local Variable + +```fortran + CHARACTER (LEN = 1) :: digit + INTEGER :: i +``` +---- +```mlir + %len = constant 1 : i32 + %digit = fir.alloca !fir.char<1>, %len : !fir.ref> + %i = fir.alloca i32 : !fir.ref +``` + +Note that in MLIR, the `%` sigil denotes an ssa-value, the `@` sigil +denotes a global symbol, and the `!` sigil denotes a type. + +##### Process lifetime variable + +```fortran + COMMON /X/ A(10),B(10) + + MODULE mymod + INTEGER a + + SUBROUTINE subr() + REAL, SAVE :: s + DATA s/12.0/ +``` +---- +```mlir + fir.global @common_x : tuple, !fir.array<10 : f32>> {} + + fir.global @mymod_a : i32 {} + + fir.global @subr_s : f32 { + constant 12.0 : f32 + } +``` + +The empty initializer region could mean these variables are placed in the +`.bss` section. + +#### Other non-executable statements + +These statements will define other properties of how the Fortran gets +lowered. For example, a variable in a `COMMON` block needs to reside in a +`fir.global`, or the structure of a derived type (user-defined record), +which would be reflected in a `!fir.type`. + +#### A note on TYPEs + +A FIR type is an synthesis of the Fortran concepts of type, attributes, and +type parameters. + +##### Intrinsic types + +For Fortran intrinsic types, there is a direct translation to a FIR type. + +```fortran + REAL(4) a + COMPLEX(8) b + CHARACTER(1,LEN=4) c + LOGICAL(1) d + INTEGER(4) e + + CHARACTER(1,LEN=*) f +``` +---- +```mlir + %a = ... : !fir.real<4> + %b = ... : !fir.complex<8> + %c = ... : !fir.array<4:!fir.char<1>> + %d = ... : !fir.logical<1> + %e = ... : !fir.int<4> + + %f_data = ... : !fir.ref>> + %f_len = ... : i32 + %f = fir.emboxchar %f_data, %f_len : !fir.boxchar<1> +``` + +The bridge will have a mapping of what the front-end kind value must map to +in the internal representation. For example, the f18 front-end maps kind +values for integers to the size in bytes of the integer representation. +Such mappings must be provided for all intrinsic type kind values. + +The Fortran `CHARACTER` variable, `f`, is a bit more complicated as there +is both a reference to a buffer (that contains the characters) and an +extra, assumed length, `LEN` type parameter to keep track of the length of +the buffer. The buffer is a sequence of `!fir.char<1>` values in memory. +The pair, `(buffer, len)`, may be boxed in a `!fir.boxchar<1>` type +object. + +##### Derived types + +Fortran also has derived types and these are supported with a more +elaborate record syntax. + +```fortran + TYPE :: person + CHARACTER(LEN=20) :: name + INTEGER :: age + END TYPE + + TYPE(person) :: george +``` +---- +```mlir + %george = ... : !fir.type>, age : i32}> +``` + +Fortran allows the compiler to reorder the fields in the derived type. +`SEQUENCE` can be used to disable reordering. (Name mangling can provide a +compile-time distinction, as needed.) + +Fortran allows a derived type to have type parameters. There are `KIND` +type parameters and `LEN` type parameters. A `KIND` type parameter is a +compile-time known constant. As such, it is possible for the compiler +implementation to create a distinct type for each set of `KIND` type +parameters (by name mangling, for instance). + +The `LEN` type parameters are runtime constant and not necessarily known at +compile-time. These values must be provided when constructing a value of +derived type in FIR, just as regular fields must be provided. (That does +not preclude an optimizer from eliminating unused `LEN` parameters.) + +Because of Fortran's `LEN` type parameters, an implementation is allowed to +defer the size and layout of an entity of derived type until runtime. + +Lowering may also exploit ad hoc product types created as needed. This can +be done using the standard dialect `tuple` type. + +##### Arrays + +An entity with type _T_ and a `DIMENSION` attribute is an array with +elements of type _T_ in Fortran. + +```fortran + INTEGER arr + DIMENSION arr(10,20) +``` +---- +```mlir + %arr = ... : !fir.array<10x20 : i32> +``` + +A FIR array is laid out in column-major order exactly like a Fortran array. + +##### Pointer and reference types + +The attribute `POINTER` can be used similarly to create a pointer entity. +The `ALLOCATABLE` attribute is another Fortran attribute that can be used +to indicate an entity's storage is to be allocated at runtime. As mentiond +previosuly, Fortran uses pass-by-reference calling semantics too. + +```fortran + INTEGER, POINTER :: ptr + REAL, ALLOCATABLE, DIMENSION(1000) :: al + + INTERFACE + SUBROUTINE fun(ptr, al) + INTEGER, POINTER :: p + REAL, ALLOCATABLE :: a + END SUBROUTINE + END INTERFACE +``` +---- +```mlir + %ptr = ... : !fir.ptr + %al = ... : !fir.heap> + + func @fun(!fir.ref>, !fir.ref>) +``` + +Note that references to pointers and heap allocatables are +allowed. However, a pointer/heap cannot point directly to a pointer/heap. + +```mlir + %err1 = ... : !fir.ptr> // Invalid type + %err2 = ... : !fir.heap> // Invalid type +``` + +Note that a value of function type is also considered a reference. + +```mlir + %fun = ... : (i32, f64) -> i1 // %fun is a reference to a func object +``` + +##### Boxed types + +Boxed types are reference types. A boxed entity is implicitly located in +memory. The only way to construct a boxed value is by providing a memory +reference type, discussed above. Any reference can be emboxed. + +There are additionally, two special-purpose box types. A `!fir.boxchar` +value is a `CHARACTER` variable (in memory) including both a pointer to the +buffer and the `LEN` type parameter. `boxchar` was discussed above. + +The second special case is the `!fir.boxproc` type. A Fortran internal +procedure can reference variables in its host's scope. Fortran also allows +pointers to procedures. A value of type `!fir.boxproc` then is a pair of +references, one for the procedure pointer and the other a pointer to a +tuple of host associated values. + +```fortran + SUBROUTINE host + REAL X + PROCEDURE(), POINTER :: procptr + ... + procptr => intern + ... + CALL procptr + CONTAINS + SUBROUTINE intern + X = ... +``` +---- +```mlir + func @host() { + %x = ... : !fir.ref + ... + %bag_val = fir.insert_value %b, %x, %0 : ... -> tuple, ...> + %bag = ... : !fir.ref, ...>> + fir.store %bag_val to %bag : !fir.ref, ...>> + %procptr = fir.emboxproc @intern, %bag : ... -> !fir.boxproc<() -> ()> + ... + fir.call %procptr() : () -> () +``` + +Here, the call to the boxed procedure implicitly passes the extra argument, the +reference to `%bag`, which contains the value of the variable `x`. + +##### Miscellaneous types + +Fortran uses triple notation to describe array sections, strided views of +multidimensional arrays. These sections can be captured using the +`fir.gendims` instruction which produces a value of type `!fir.dims`. + +```fortran + DIMENSION (10,10) a + ... a(2:6:2,1:7:4) ... +``` +---- +```mlir + // the following line is pseudocode + %1 = fir.gendims 2,6,2, 1,7,4 : !fir.dims<2> +``` + +Fortran also allows the implementation to reorder fields in a derived +type. Furthermore, the sizes of these fields and the layout may be left up +to the runtime. This could mean that the backend needs to generate runtime +calls to determine the offsets and sizes of fields. + +```fortran + TYPE ding(k) + ... + TYPE(T(k)) :: field_name +``` +---- +```mlir + %2 = fir.field("field_name") : !fir.field +``` + +When lowering a boxed value, the compiler may need to test what the exact +type of the value is at runtime. (For example, when generating code for +`SELECT TYPE`.) + +```fortran + CLASS(*) :: x + SELECT TYPE (x) + ... +``` +---- +```mlir + %1 = fir.box_tdesc %x : (!fir.box) -> !fir.tdesc +``` + +The `none` type is used when the entity has unlimited polymorphic type. See +below for a larger example of `SELECT TYPE`. + +### Executable statements + +The main purpose of lowering is to lower all the executable statements from +Fortran into FIR in a semantics preserving way. + +#### Substrings + +```fortran + ID(4:9) +``` +---- +```mlir + %id = ... : !fir.ref>> + %1 = fir.coordinate_of %id, %c3 : ... -> !fir.ref> + %2 = fir.emboxchar %1, %c5 : ... -> !fir.boxchar<1> +``` + +#### Structure components + +```fortran + scalar_parent%scalar_field +``` +---- +```mlir + %sf = fir.field("scalar_field") : !fir.field + %1 = fir.coordinate_of %scalar_parent, %sf : ... -> !fir.ref +``` + +#### Type parameters + +```fortran + TYPE ding(dim) + INTEGER, LEN :: dim + REAL :: values(dim) + END TYPE ding + + ding(x) :: a_ding + ... a_ding%dim ... +``` +---- +```mlir + %1 = fir.len_param_index("dim") : !fir.field + %2 = fir.coordinate_of %a_ding, %1 : ... -> !fir.ref + %3 = fir.load %2 : !fir.ref +``` + +#### Arrays + +```fortran + ... A ... ! whole array + ... B(4) ... ! array element + ... C(1:10) ... ! array section + ... D(1:10:2) ... ! array section with stride + INTEGER, DIMENSION :: V(4) + ... E(V) ... ! array section with vector subscript +``` +---- +```mlir + %1 = fir.load %a : !fir.ref> + + %2 = fir.extract_element %b, %c4 : (!fir.array, i32) -> f32 + + %3 = fir.coordinate_of %c, %c1 : (!fir.ref>, i32) -> !fir.ref + %4 = fir.convert %3 : (!fir.ref) -> !fir.ref> + %5 = fir.load %4 : (!fir.ref>) -> !fir.array<10:f32> + + %6 = fir.gendims %c1, %c10, %c2 : (i32, i32, i32) -> !fir.dims<1> + %7 = fir.embox %d, %6 : (!fir.ref>, !fir.dims<1>) -> !fir.embox> + + // create a temporary to hold E(V) + %v = ... : !fir.array<4:i32> + %8 = fir.alloca !fir.array<4:f32> : !fir.ref> + fir.loop %i = %c1 to %c4 unordered { + %9 = fir.extract_value %v, %i : (!fir.array<4:i32>, index) -> i32 + %10 = fir.extract_value %e, %9 : (!fir.array, i32) -> f32 + %11 = fir.coordinate_of %8, %i : (!fir.ref>, index) -> !fir.ref + fir.store %10 to %11 : !fir.ref + } +``` + +In the fourth case, lowering could also create a temporary and copy the +values from the section `D(1:10:2)` into it, but the preference should be +to defer copying data until it is necessary (as in the fifth non-affine +case, `E(V)`). + +#### Image selector + +```fortran + REAL :: A(10)[5,*] + + ... A(:)[1,4] ... ! selects image 16 (if available) +``` +---- +```mlir + %1 = fir.call @runtime_fetch_array(%a, %c_1, %c_4, ...) : (!fir.box>, i32, i32, ...) -> !fir.ref> +``` + +#### Dynamic association + +```fortran + ALLOCATE (x(n), b(-3:m, 0:9)) + + NULLIFY (p) + + DEALLOCATE (x, b) +``` +---- +```mlir + %x = fir.allocmem f32, %n : !fir.heap> + + %c4 = constant 4 : i32 + %1 = addi %m, %c4 : i32 + %2 = constant 10 : i32 + %b = fir.allocmem f32, %1, %2 : !fir.heap> + + %zero = constant 0 : i64 + %null = fir.convert %zero : (i64) -> !fir.ptr + fir.store %null to %p : !fir.ref> + + fir.freemem %x : !fir.heap> + fir.freemem %b : !fir.heap> +``` + +#### Basic operators + +Operators like `**`, `*`, `/`, etc. will be lowered into standard dialect +operations or runtime calls as needed. + +```fortran + a * b + c .LE. d +``` +---- +```mlir + %0 = mulf %a, %b : f32 + %1 = cmp "le" %c, %d : (f32, f32) -> i1 +``` + +#### Calls + +```fortran + CALL foo(v1) + ... func(v2, v3) ... + + pp => bar + CALL pp(v4) + + CALL object%method(arg) +``` +---- +```mlir + fir.call @foo(%v1) : (!fir.ref) -> () + %1 = fir.call @func(%v2, %v3) : (!fir.ref) -> i64 + + %pp = fir.address_of(@bar) : ((!fir.ref) -> ()) -> !fir.ref<(!fir.ref) -> ()> + fir.icall %pp(%v4) : (!fir.ref) -> () + + fir.dispatch "method"(%object, %arg) : (!fir.box>, !fir.ref) -> () +``` + +There are two modes of argument passing in Fortran: calls that are "Fortran +77" style and use an implicit interface, and calls that require an +interface. In FIR, this translates to passing a simple reference to an +entity's data versus passing a boxed reference value. The following calls +illustrate this distinction. + +```fortran + SUBROUTINE sub1(a) + INTEGER :: a(10,10) ! F77 style + ... + INTERFACE + SUBROUTINE sub2(a) + INTEGER :: a(:,:) ! assumed shape + ... + PROGRAM p + INTEGER :: a(10,10) + CALL sub1(a) + CALL sub2(a) +``` +---- +```mlir + func @sub1(!fir.ref>) -> () + func @sub1(!fir.box>) -> () + + func @_QP_p() { + %c1 = constant 1 : i32 + %c10 = constant 10 : i32 + %a1 = fir.alloca !fir.array<10x10:i32> : !fir.ref> + fir.call @sub1(%a1) : (!fir.ref>) -> () + %1 = fir.gendims %c1, %c10, %c1, %c1, %c10, %c1 : (i32,i32,i32,i32,i32,i32) -> !fir.dims<2> + %a2 = fir.embox %a1, %1 : (!fir.ref>, !fir.dims<2>) -> !fir.box> + fir.call @sub2(%a2) : (!fir.box>) -> () +``` + +When lowering into FIR, the bridge must explicitly perform any allocation, +copying, deallocation, and finalization on temporary entities as required +by the Fortran standard, preserving the copy-in copy-out calling +convention. + +#### Parentheses (10.1.8) + +```fortran + (a + b) + (a + c) ! cannot rewrite as (2 * a) + b + c +``` +---- +```mlir + %1 = addf %a, %b : f32 + %2 = fir.no_reassoc %1 : f32 // to prevent reassociation + %3 = addf %a, %c : f32 + %4 = fir.no_reassoc %3 : f32 + %5 = addf %2, %4 : f32 +``` + +One must also specify to LLVM that these operations will not be reassociated. + +#### Assignment + +```fortran + scalar = e1 ! intrinsic scalar assignment + array = e2 ! intrinsic array assignment + object = e3 ! defined assignment + pntr => e4 ! pointer assignment + pproc => func ! procedure pointer assignment +``` +---- +```mlir + %e1 = ... : f32 + fir.store %e1 to %scalar : !fir.ref + + %e2 = ... : !fir.array<10x10 : i32> + fir.store %e2 to %array : !fir.ref> + + %e3 = ... !fir.ref + %object = ... !fir.ref + fir.call @defd_assn(%object, %e3) : ... -> () + + %e4 = ... : !fir.ptr + %pntr = ... : !fir.ref> + fir.store %e4 to %pntr : !fir.ref> + + @func(i32, i32) -> i32 + %fn = fir.address_of(@func) : ((i32, i32) -> i32) -> !fir.ptr<(i32, i32) -> i32> + %pproc = ... : !fir.ref i32>> + fir.store %fn to %pproc : !fir.ref i32>> +``` + +#### Masked assignment + +```fortran + WHERE (arr < threshold) + arr = arr + increment + ELSEWHERE + arr = threshold + END WHILE +``` +---- +```mlir + %arr = ... : !fir.array + %threshold = ... : !fir.array + fir.loop %i = %c1 to %size { + %arr_i = fir.extract_value %arr, %i : ... -> !T + %threshold_i = fir.extract_value %threshold, %i : ... -> !T + %1 = cmp "lt" %arr_i, %threshold_i : ... -> i1 + fir.where %1 { + %2 = addf %arr_i, %increment : !T + %3 = fir.coordinate_of %arr, %i : ... -> !fir.ref + fir.store %2 to %3 : !fir.ref + } otherwise { + %4 = fir.coordinate_of %arr, %i : ... -> !fir.ref + fir.store %threshold_i to %4 + } + } +``` + +#### FORALL + +```fortran + FORALL (i = 1:100) + a(i) = b(i) / c(i) + END FORALL +``` +---- +```mlir + fir.loop %i = %c1 to %c100 unordered { + %1 = fir.extract_value %b, %i : (!fir.array, index) -> f32 + %2 = fir.extract_value %c, %i : (!fir.array, index) -> f32 + %3 = divf %1, %2 : f32 + %4 = fir.coordinate_of %a, %i : (!fir.ref>, index) -> !fir.ref + fir.store %3 to %4 : !fir.ref + } +``` + +#### ASSOCIATE construct + +```fortran + ASSOCIATE (z => EXP(-(x**2+y**2)) * COS(theta)) + CALL foo(z) + END ASSOCIATE +``` +---- +```mlir + %1 = ... : f32 + %2 = fir.call @exp(%1) : (f32) -> f32 + %3 = fir.load %theta : !fir.ref + %4 = fir.call @cos(%3) : (f32) -> f32 + %5 = mulf %2, %4 : f32 + fir.store %5 to %z : !fir.ref + fir.call @foo(%z) : (!fir.ref) -> () +``` + +#### DO construct + +```fortran + DIMENSION a(10,10,10), b(10,10,10) + + DO i = 1, m + DO j = 1, n + c(i,j) = dot_prod(a(i,j,:), b(:,i,j)) + END DO + END DO +``` +---- +```mlir + %c1 = constant 1 : index + %c10 = constant 10 : index + %c100 = constant 100 : index + %c1000 = constant 1000 : index + %1 = fir.gendims %c1, %c1000, %c100 : !fir.dims<1> + %2 = fir.gendims %c1, %c10, %c1 : !fir.dims<1> + + fir.loop %i = %c1 to %m { + fir.loop %i = %c1 to %n { + %13 = fir.coordinate_of %a, %i, %j : !fir.ref> + %14 = fir.embox %13, %1 : (!fir.ref>, !fir.dims<1>) -> !fir.box> + %15 = fir.coordinate_of %b, %c1, %i, %j : !fir.ref + %16 = fir.convert %15 : (!fir.ref) -> !fir.ref> + %17 = fir.embox %16, %2 : (!fir.ref>, !fir.dims<1>) -> !fir.box> + %18 = fir.call @dot_prod(%14, %17) : (!fir.box>, !fir.box>) -> f32 + %19 = fir.coordinate_of %c, %i, %j : (!fir.box>, index, index) -> !fir.ref + fir.store %18 to %19 : !fir.ref + } + } +``` + +In this lowering, the array sections from the arrays `a` and `b` are _not_ +copied to a temporary memory buffer, but are instead captured in boxed +values (`%14` and `%17`). + +#### IF construct + +```fortran + IF (a > 0) THEN + ... + ELSE + ... + END IF +``` +---- +```mlir + %1 = ... : i1 + cond_br %1, ^bb1(%2:i32), ^bb2(%3:i32) +``` + +#### SELECT CASE construct + +```fortran + SELECT CASE (p) + CASE (1, 3:5) + ... + CASE (:-1) + ... + CASE (10:) + ... + CASE DEFAULT + ... + END SELECT CASE +``` +---- +```mlir + fir.select_case %p : i32 [#fir.point,%c1,^bb1, #fir.interval,%c3,%c5,^bb1, #fir.upper,%cn1,^bb2, #fir.lower,%c10,^bb3, unit,^bb4] +``` + +#### SELECT RANK construct + +```fortran + SELECT RANK (p) + RANK (2) + ... + RANK (*) + ... + RANK DEFAULT + ... + END SELECT RANK +``` +---- +```mlir + fir.select_rank %p : i32 [2,^bb1(%1:f32), -1,^bb2, unit,^bb3(%2:f32,%3:i32)] +``` + +#### SELECT TYPE construct + +```fortran + SELECT TYPE (p) + TYPE IS (type_a) + ... + CLASS IS (super_b) + ... + CLASS DEFAULT + ... + END SELECT TYPE +``` +---- +```mlir + fir.select_type %p : !fir.box [#fir.instance>,^bb_1(%1:i32,%2:i64), #fir.subsumed>,^bb_2(%3:f32,%4:f64,%5:i32), unit,^bb_3] +``` +---- +```mlir + %type_a_desc = fir.gentypedesc !fir.type : !fir.tdesc> + %super_b_desc = fir.gentypedesc !fir.type : !fir.tdesc> + %11 = fir.box_tdesc %p : (!fir.box) -> !fir.tdesc + %12 = cmp "eq" %11, %type_a_desc : (!fir.tdesc, !fir.tdesc>) -> i1 + cond_br %2, ^bb1(%1:i32,%2:i64), ^bb1b(%3:f32,%4:f64,%5:i32) + ^bb1(%a1,%a2 : i32,i64): + ... + ^bb1b(%b1,%b2,%b3 : f32,f64,i32): + %13 = fir.call @is_subtype_of(%11, %super_b_desc) : ... -> i1 + cond_br %13, ^bb2(%b1,%b2,%b3), ^bb3 + ^bb2(%b1,%b2,%b3 : f32,f64,i32): + ... + ^bb3: + ... +``` + +#### Jumping statements + +```fortran + STOP + ERROR STOP + FAIL IMAGE + CONTINUE loop + EXIT a_construct + GOTO label1 + GOTO (label2,label3,label4), i +``` +---- +```mlir + fir.call @stop() + fir.unreachable + + fir.call @error_stop() + fir.unreachable + + fir.call @fail_image() + fir.unreachable + + br ^bb_continue + + br ^bb_exit + + br ^bb_label1 + + fir.select %i : i32 [1,^bb_label2(%1:i32), 2,^bb_label3, 3,^bb_label4, unit,^fallthru] + ^fallthru: +``` + diff --git a/documentation/FIRLangRef.md b/documentation/FIRLangRef.md index 4f0438759b65..533bd4353c59 100644 --- a/documentation/FIRLangRef.md +++ b/documentation/FIRLangRef.md @@ -66,7 +66,7 @@ can be trivially rewritten to `i16`. ## Fortran Derived Types -
!fir.type<derived-type-name [ (len-param-list) ] [ {field-id-list ] }>
+
!fir.type<derived-type-name [ (len-param-list) ] [ {field-id-list} ] >
    where len-param := len-param-name : integer-type
          field-id := field-name : type
@@ -89,11 +89,11 @@ attributes into the constraints of FIR's static type system.
This is the type of a Fortran procedure (function or subroutine) and is the -same as the standard dialect. The void type is simply `none`. +same as the standard dialect. The void type is simply `()`. ### Sequence Type -
!fir.array< extent-xlist : element-type>
+
!fir.array<extent-xlist : element-type>
    where extent := integer-constant | ? | *
@@ -338,7 +338,7 @@ A terminator for a simple switch like control flow. Example: - To do + 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 ] #### `fir.select_case` @@ -354,7 +354,7 @@ A terminator for the SELECT CASE construct. Example: - To do + 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] #### `fir.select_rank` @@ -364,7 +364,7 @@ A terminator for the SELECT RANK construct. Example: - To do + 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 ] #### `fir.select_type` @@ -379,7 +379,7 @@ A terminator for the SELECT TYPE construct. Example: - To do + 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 ] #### `fir.unreachable` @@ -450,18 +450,23 @@ Example: #### `fir.unbox` -Syntax: fir.unbox box-value : ( arg-type ) -> tuple-of-box +Syntax: fir.unbox box-value : ( arg-type ) -> (!fir.ref<Tx>, iv, iw, !fir.tdesc<Tx>, iy, !fir.dims<z>) -Unbox a boxed value into a tuple of its component data. +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). Example: ```mlir %40 = fir.call @foo() : !fir.box> - %41 = fir.unbox %40 : (!fir.box>) -> (!fir.ref>, i32, i32, i32, !fir.tdesc>, !fir.dims<4>) + %41 = fir.unbox %40 : (!fir.box>) -> (!fir.ref>, i32, i32, !fir.tdesc>, i32, !fir.dims<4>) ``` -Note: the exact content of the returned tuple type is to be determined. +Note: the exact type and content of the returned multiple value is still to +be determined and may change. #### `fir.unboxchar` @@ -580,7 +585,7 @@ Example: %55 = fir.box_isarray %40 : (!fir.box>, i32) -> i1 ``` -#### fir.box_isptr +#### `fir.box_isptr` Syntax: fir.box_isptr box-value : ( box-type ) -> i1 @@ -643,7 +648,7 @@ Return the host context of a boxproc value, if any. Example: ```mlir - %60 = fir.boxproc_host %47 : (!fir.boxproc<() -> none>) -> (!fir.ref<() -> none>, !fir.ref<(f32, i64)>) + %60 = fir.boxproc_host %47 : (!fir.boxproc<() -> none>) -> (() -> none, !fir.ref<(f32, i64)>) ``` The content and type of _host-context_ is to be determined. @@ -683,7 +688,7 @@ Example: #### `fir.field_index` -Syntax: fir.field_index "field-name" : !fir.field +Syntax: fir.field_index ("field-name") : !fir.field Compute the field offset of a particular named field in a derived type. Note: it is possible in Fortran to write code that can only determine @@ -693,7 +698,7 @@ runtime. Example: ```mlir - %62 = fir.field_index "member_1" : !fir.field + %62 = fir.field_index ("member_1") : !fir.field ``` #### `fir.gendims` @@ -709,12 +714,12 @@ Example: ```mlir %c1 = constant 1 : i32 %c10 = constant 10 : i32 - %63 = fir.gendims(%c1,%c10,%c1) : (i32,i32,i32) -> !fir.dims<1> + %63 = fir.gendims %c1,%c10,%c1 : (i32,i32,i32) -> !fir.dims<1> ``` #### `fir.insert_value` -Syntax: fir.insert_value ( entity , value , index-field-list ) ( entity-type , value-type , index-field-type-list ) -> entity-type +Syntax: fir.insert_value entity , value , index-field-list : ( entity-type , value-type , index-field-type-list ) -> entity-type Insert a value into an entity with a type composed arrays and/or derived types. Returns a new value of the same type as _entity_. @@ -739,7 +744,7 @@ The above is a possible translation of the following Fortran code sequence. #### `fir.len_param_index` -Syntax: fir.len_param_index len-param-name : !fir.field -> index +Syntax: fir.len_param_index ("len-param-name") : !fir.field Compute the LEN type parameter offset of a particular named parameter in a derived type. @@ -747,7 +752,7 @@ derived type. Example: ```mlir - %62 = fir.len_param_index("param_1") : !fir.field -> index + %62 = fir.len_param_index("param_1") : !fir.field ``` ### Generalized Control Flow Ops @@ -806,7 +811,7 @@ generalized and not restricted to affine loop nests. Example: ```mlir - %78 = fir.call %75(%74) : !fir.ref + %78 = fir.icall %75(%74) : !fir.ref fir.where %56 { fir.store %76 to %78 : !fir.ref } otherwise { @@ -818,13 +823,24 @@ Example: Syntax: fir.call callee ( arg-list ) : func-type -Call the specified function or function reference. +Call the specified function. Example: ```mlir - %89 = fir.call %funcref(%arg0) : !FT1 - %90 = fir.call @function(%arg1, %arg2) : !FT2 + %90 = fir.call @function(%arg1, %arg2) : (!fir.ref, !fir.ref) -> f32 +``` + +#### `fir.icall` + +Syntax: fir.icall callee ( arg-list ) : func-type + +Call the specified function reference. + +Example: + +```mlir + %89 = fir.icall %funcref(%arg0) : (!fir.ref) -> f32 ``` #### `fir.dispatch` @@ -839,11 +855,23 @@ associated with the first argument. Example: ```mlir - %91 = fir.dispatch "methodA"(%89, %90) : !FT3 + %91 = fir.dispatch "methodA"(%89, %90) : (!fir.box>, !fir.ref) -> i32 ``` ### Other Ops +#### `fir.address_of` + +Syntax: fir.address_of (@symbol) : T + +Converts a symbol to an SSA-value. + +Example: + +```mlir + %func = fir.address_of(@func) : !fir.ref<(!fir.ref) -> ()> +``` + #### `fir.convert` Syntax: fir.convert ssa-value : ( T ) -> U @@ -857,8 +885,8 @@ are the same type, this instruction is a NOP. Example: ```mlir - %92 = fir.call @foo() : i64 - %93 = fir.convert %92 : i64 -> i32 + %92 = fir.call @foo() : () -> i64 + %93 = fir.convert %92 : (i64) -> i32 ``` The above conversion truncates a 64-bit integer value to 32-bits. @@ -892,9 +920,9 @@ argument. Example: ```mlir - %98 = mulf %96,%97 : (f32,f32) -> f32 + %98 = mulf %96,%97 : f32 %99 = fir.no_reassoc %98 : f32 - %100 = addf %99,%95 : (f32,f32) -> f32 + %100 = addf %99,%95 : f32 ``` The presence of this operation prevents any local optimizations. In the @@ -956,7 +984,7 @@ Example: #### `fir.dt_entry` -Syntax: fir.dt_entry "method-id" , callee : func-type +Syntax: fir.dt_entry "method-id" , callee A dispatch table entry is a mapping in a dispatch table that binds a @@ -967,146 +995,8 @@ Example: ```mlir fir.dispatch_table @_QDTMquuzTfoo { - fir.dt_entry "method1", @_QFNMquuzTfooPmethod1AfooR : !FT1 - fir.dt_entry "method2", @_QFNMquuzTfooPmethod2AfooII : !FT2 - } -``` - -# Optimization Examples - -## Loop Transformations - -To do: normalization. fission, fusion, interchange, index-set splitting, -reversal, skewing, tiling, unswitching. unrolling, vectorization, -inversion, and code motion. - -## Data Choreography - -To do: alignment, allocation, index mapping, copy elimination - - -## Function Calls - -### Inlining - -While LLVM has a low-level inlining optimization, we may want to have a -high-level inliner as well. Without the semantics knowledge, the low-level -optimizer may have to "look into" opaque calls to the Fortran runtime to -accomplish the same level of optimization. - -For example, say we have some code that is passing an assumed-size array, -`%arr`, to a rather large function, `@foo`, which takes an assumed-rank -array dummy argument. Let's say that there is a Fortran runtime routine -that the `fir.select_rank` calls to determine the rank of the argument, and -that if the effective argument was an assumed-size array that routine will -return the special value `-1`. - - -```mlir - func @foo(%dummy : !fir.box>) { - fir.select_rank %dummy, 1, ^bb1 ... n, ^bbn, -1, ^bbstar, - undef, ^bbdef - ^bb1: - ... - ... - ^bbn: - ... - ^bbstar: - return - ^bbdef: - ... - ... - return - } - - !BoxAT = type !fir.box> - %ref = ... : !fir.ref> - %dims = fir.gendims(1, -1, 1) : !fir.dims<1> ;assumed-size - %arr = fir.embox %ref, %dims : !BoxAT - %result = fir.call @foo(%arr) - %next = ... -``` - -At the LLVM IR level, the compiler will generate code that calls this -opaque Fortran runtime library routine to determine the rank of the boxed -argument, and thus prevent the LLVM inliner from knowing that the case that -must be chosen here is a NOP. The low-level inliner only sees the large -`@foo` routine. With a high-level inliner, the compiler can "see around the -corner" and perform the inlining. After inlining and DCE, this example -would reduce to nothing more than the following. - - -```mlir - %ref = ... : !fir.ref> - %next = ... -``` - -It is clear that this result will be better in terms of performance. - - -### Specialization - -Specialization is the optimization of cloning a function into multiple -copies, each copy of which has a distinctive property. For example, a -Fortran code may have a type-bound procedure, `subr`, that takes a -`CLASS(foo)` type argument. But the compiler may decide to version this -type-bound procedure into two copies, one taking the `CLASS(foo)` argument -(as before) and another taking a `TYPE(bar)`, where `bar` is a subclass of -`foo`. - -The signature of the original function might look something like the -following. - -```mlir - !foo_type = type ... - func @_QFBsubrACfoo(%dummy : !fir.box) -``` - -After specialization, as described above, the compiler could add the -signature. - - -```mlir - !bar_type = type ... - func @_QFNsubrATbar(%dummy : !fir.ref) -``` - -Since the exact type of `%dummy` is known by the compiler, the second -function is passed an unboxed reference value, which may help reduce, say, -the call overhead. Further analysis might be able to prove that `subr` -does not modify the entity referenced by `%dummy` and specialize once -again, giving a function that expects `%dummy` to be passed as a value. - -```mlir - func @_QFNsubrAVbar(%dummy : !bar_type) -``` - - -### Devirtualization - -Devirtualization is the optimization of converting a virtual call -(dispatch) to a regular call. - -Let's say we generate the following for a virtual call to `method1`, which -for the type `foo` in module `quark` is the type-bound procedure -`@foo_method_one`. - -```mlir - fir.dispatch_table @_QDT_Mquark_Tfoo { - fir.dt_entry "method1", @foo_method_one - fir.dt_entry "method2", @foo_method_two + fir.dt_entry "method1", @_QFNMquuzTfooPmethod1AfooR + fir.dt_entry "method2", @_QFNMquuzTfooPmethod2AfooII } - - %result = fir.dispatch method1(%box) -``` - -If the compiler can prove the type of `%box` must be `type(foo)` rather -than `class(foo)`, devirtualization would replace the dispatch instruction -with - -```mlir - %result = fir.call @foo_method_one(%box) ``` -This eliminates the extra indirection through the dispatch table at -runtime. diff --git a/include/fir/.clang-format b/include/fir/.clang-format new file mode 100644 index 000000000000..392e20189554 --- /dev/null +++ b/include/fir/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes \ No newline at end of file diff --git a/include/fir/CMakeLists.txt b/include/fir/CMakeLists.txt index ad0afd924f90..9528b1abffb0 100644 --- a/include/fir/CMakeLists.txt +++ b/include/fir/CMakeLists.txt @@ -2,6 +2,3 @@ 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) - -add_subdirectory(Tilikum) -add_subdirectory(Transforms) diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index 4d126f2cf615..c56f60d9da5f 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -120,37 +120,37 @@ class fir_SimpleOp traits> } def fir_AllocateOpBuilder : OpBuilder< - "Builder *, OperationState &result, Type resultType," - "ArrayRef size = {}, ArrayRef attributes = {}", + "Builder *builder, OperationState &result, Type inType," + "ArrayRef sizes = {}, ArrayRef attributes = {}", [{ - result.addTypes(resultType); - result.addOperands(size); + result.addTypes(getRefTy(inType)); + result.addAttribute("in_type", builder->getTypeAttr(inType)); + result.addOperands(sizes); for (auto namedAttr : attributes) result.addAttribute(namedAttr.first, namedAttr.second); }]>; def fir_NamedAllocateOpBuilder : OpBuilder< - "Builder *builder, OperationState &result, Type resultType," - "StringRef name, ArrayRef size = {}," - "ArrayRef attributes = {}", + "Builder *builder, OperationState &result, Type inType, StringRef name," + "ArrayRef sizes = {}, ArrayRef attributes = {}", [{ + result.addTypes(getRefTy(inType)); + result.addAttribute("in_type", builder->getTypeAttr(inType)); result.addAttribute("name", builder->getStringAttr(name)); - result.addTypes(resultType); - result.addOperands(size); + result.addOperands(sizes); for (auto namedAttr : attributes) result.addAttribute(namedAttr.first, namedAttr.second); }]>; def fir_OneResultOpBuilder : OpBuilder< - "Builder *, OperationState &result, Type resultType, " + "Builder *, OperationState &result, Type resultType," "ArrayRef operands, ArrayRef attributes = {}", [{ if (resultType) result.addTypes(resultType); result.addOperands(operands); - for (auto namedAttr : attributes) { + for (auto namedAttr : attributes) result.addAttribute(namedAttr.first, namedAttr.second); - } }]>; class fir_OneResultOp traits = []> : @@ -178,22 +178,7 @@ class fir_AllocatableBaseOp traits = []> : class fir_AllocatableOp traits =[]> : fir_AllocatableBaseOp, fir_TwoBuilders, - Arguments<(ins Variadic:$shape)> { -} - -// 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 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. - }]; - + Arguments<(ins TypeAttr:$in_type, Variadic:$shape)> { let parser = [{ M::Type intype; if (parser.parseType(intype)) @@ -205,6 +190,7 @@ def fir_AllocaOp : fir_AllocatableOp<"alloca"> { llvm::SmallVector operands; if (parser.parseOperandList(operands, M::OpAsmParser::Delimiter::None) || + parser.parseOptionalAttributeDict(result.attributes) || parser.resolveOperands(operands, builder.getIndexType(), parser.getNameLoc(), result.operands)) return M::failure(); @@ -215,15 +201,31 @@ def fir_AllocaOp : fir_AllocatableOp<"alloca"> { return M::failure(); return M::success(); }]; + let printer = [{ p << getOperationName() << ' ' << getAttr("in_type"); for (auto sh : getShapeOperands()) { p << ", "; p.printOperand(sh); } - p << " : " << getType(); p.printOptionalAttrDict(getAttrs(), {"in_type"}); + p << " : " << getType(); }]; +} + +// 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 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. + }]; + let verifier = [{ M::Type inType = getAttrOfType("in_type").getValue(); M::Type outType = getType(); @@ -241,6 +243,7 @@ def fir_AllocaOp : fir_AllocatableOp<"alloca"> { operand_range getShapeOperands() { return {operand_begin(), operand_end()}; } + static mlir::Type getRefTy(mlir::Type ty); }]; } @@ -267,6 +270,7 @@ def fir_LoadOp : fir_OneResultOp<"load", [NoSideEffect]>, M::Type type; M::OpAsmParser::OperandType oper; if (parser.parseOperand(oper) || + parser.parseOptionalAttributeDict(result.attributes) || parser.parseColonType(type) || parser.resolveOperand(oper, type, result.operands)) return M::failure(); @@ -276,11 +280,12 @@ def fir_LoadOp : fir_OneResultOp<"load", [NoSideEffect]>, return M::failure(); return M::success(); }]; + let printer = [{ p << getOperationName() << ' '; p.printOperand(memref()); - p << " : " << memref()->getType(); p.printOptionalAttrDict(getAttrs(), {}); + p << " : " << memref()->getType(); }]; let extraClassDeclaration = [{ @@ -305,6 +310,7 @@ def fir_StoreOp : fir_Op<"store", []>, if (parser.parseOperand(oper) || parser.parseKeyword("to") || parser.parseOperand(store) || + parser.parseOptionalAttributeDict(result.attributes) || parser.parseColonType(type) || parser.resolveOperand(oper, elementType(type), result.operands) || @@ -312,13 +318,14 @@ def fir_StoreOp : fir_Op<"store", []>, return M::failure(); return M::success(); }]; + let printer = [{ p << getOperationName() << ' '; p.printOperand(value()); p << " to "; p.printOperand(memref()); - p << " : " << memref()->getType(); p.printOptionalAttrDict(getAttrs(), {}); + p << " : " << memref()->getType(); }]; let extraClassDeclaration = [{ @@ -340,13 +347,11 @@ def fir_UndefOp : fir_OneResultOp<"undefined", [NoSideEffect]> { if (parser.parseType(intype) || parser.addTypeToList(intype, result.types)) return M::failure(); - auto &builder = parser.getBuilder(); - result.addAttribute("in_type", builder.getTypeAttr(intype)); return M::success(); }]; - let printer = [{ - p << getOperationName() << ' ' << getAttr("in_type"); - }]; + + let printer = [{ p << getOperationName() << ' ' << getType(); }]; + let verifier = [{ if (auto ref = getType().dyn_cast()) return emitOpError("undefined values of type !fir.ref not allowed"); @@ -364,36 +369,6 @@ def fir_AllocMemOp : fir_AllocatableOp<"allocmem"> { be paired with `freemem` operations to avoid memory leaks. }]; - let parser = [{ - M::Type intype; - if (parser.parseType(intype)) - return M::failure(); - auto &builder = parser.getBuilder(); - result.addAttribute("in_type", builder.getTypeAttr(intype)); - if (!parser.parseOptionalComma()) { - llvm::SmallVector typeVec; - llvm::SmallVector operands; - if (parser.parseOperandList(operands, - M::OpAsmParser::Delimiter::None) || - parser.resolveOperands(operands, builder.getIndexType(), - parser.getNameLoc(), result.operands)) - return M::failure(); - } - M::Type restype; - if (parser.parseColonType(restype) || - parser.addTypeToList(restype, result.types)) - return M::failure(); - return M::success(); - }]; - let printer = [{ - p << getOperationName() << ' ' << getAttr("in_type"); - for (auto sh : getShapeOperands()) { - p << ", "; - p.printOperand(sh); - } - p << " : " << getType(); - p.printOptionalAttrDict(getAttrs(), {"in_type"}); - }]; let verifier = [{ M::Type inType = getAttrOfType("in_type").getValue(); M::Type outType = getType(); @@ -411,6 +386,7 @@ def fir_AllocMemOp : fir_AllocatableOp<"allocmem"> { operand_range getShapeOperands() { return {operand_begin(), operand_end()}; } + static mlir::Type getRefTy(mlir::Type ty); }]; } @@ -429,16 +405,18 @@ def fir_FreeMemOp : fir_Op<"freemem", []>, M::Type type; M::OpAsmParser::OperandType oper; if (parser.parseOperand(oper) || + parser.parseOptionalAttributeDict(result.attributes) || parser.parseColonType(type) || parser.resolveOperand(oper, type, result.operands)) return M::failure(); return M::success(); }]; + let printer = [{ p << getOperationName() << ' '; p.printOperand(heapref()); - p << " : " << getOperand()->getType(); p.printOptionalAttrDict(getAttrs(), {}); + p << " : " << getOperand()->getType(); }]; } @@ -815,24 +793,29 @@ def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]>, Results<(outs fir_BoxType)>, }]; let parser = [{ - mlir::FunctionType type; - llvm::SmallVector operands; - mlir::OpAsmParser::OperandType memref; + M::FunctionType type; + llvm::SmallVector operands; + M::OpAsmParser::OperandType memref; if (parser.parseOperand(memref)) - return mlir::failure(); + return M::failure(); operands.push_back(memref); if (!parser.parseOptionalComma()) { - mlir::OpAsmParser::OperandType dims; + M::OpAsmParser::OperandType dims; if (parser.parseOperand(dims)) - return mlir::failure(); + return M::failure(); operands.push_back(dims); + } else if (!parser.parseOptionalLSquare()) { + M::AffineMapAttr map; + if (parser.parseAttribute(map, "layout_map", result.attributes) || + parser.parseRSquare()) + return M::failure(); } if (parser.parseColonType(type) || parser.resolveOperands(operands, type.getInputs(), parser.getNameLoc(), result.operands) || parser.addTypesToList(type.getResults(), result.types)) - return mlir::failure(); - return mlir::success(); + return M::failure(); + return M::success(); }]; let printer = [{ @@ -841,6 +824,8 @@ def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]>, Results<(outs fir_BoxType)>, if (getNumOperands() == 2) { p << ", "; p.printOperands(dims()); + } else if (auto map = getAttr("layout_map")) { + p << " [" << map << ']'; } p << " : "; p.printFunctionalType(getOperation()); @@ -860,16 +845,16 @@ def fir_EmboxCharOp : fir_Op<"emboxchar", [NoSideEffect]>, }]; let parser = [{ - mlir::FunctionType type; - llvm::SmallVector operands; + M::FunctionType type; + llvm::SmallVector operands; if (parser.parseOperandList(operands, 2, - mlir::OpAsmParser::Delimiter::None) || + M::OpAsmParser::Delimiter::None) || parser.parseColonType(type) || parser.resolveOperands(operands, type.getInputs(), parser.getNameLoc(), result.operands) || parser.addTypesToList(type.getResults(), result.types)) - return mlir::failure(); - return mlir::success(); + return M::failure(); + return M::success(); }]; let printer = [{ @@ -975,14 +960,14 @@ def fir_BoxAddrOp : fir_SimpleOneResultOp<"box_addr", [NoSideEffect]>, }]; let parser = [{ - mlir::FunctionType type; - mlir::OpAsmParser::OperandType opnd; + M::FunctionType type; + M::OpAsmParser::OperandType opnd; if (parser.parseOperand(opnd) || parser.parseColonType(type) || parser.resolveOperand(opnd, type.getInput(0), result.operands) || parser.addTypesToList(type.getResults(), result.types)) - return mlir::failure(); - return mlir::success(); + return M::failure(); + return M::success(); }]; let printer = [{ @@ -1015,16 +1000,16 @@ def fir_BoxDimsOp : fir_Op<"box_dims", [NoSideEffect]>, }]; let parser = [{ - mlir::FunctionType type; - llvm::SmallVector operands; + M::FunctionType type; + llvm::SmallVector operands; if (parser.parseOperandList(operands, 2, - mlir::OpAsmParser::Delimiter::None) || + M::OpAsmParser::Delimiter::None) || parser.parseColonType(type) || parser.resolveOperands(operands, type.getInputs(), parser.getNameLoc(), result.operands) || parser.addTypesToList(type.getResults(), result.types)) - return mlir::failure(); - return mlir::success(); + return M::failure(); + return M::success(); }]; let printer = [{ @@ -1124,20 +1109,20 @@ def fir_CoordinateOp : fir_Op<"coordinate_of", [NoSideEffect]>, let description = [{ Determine a memory reference given a memory reference of composite type - and a list of index values. + and a list of index values. (This returns the address of a value.) }]; let parser = [{ - mlir::FunctionType type; - llvm::SmallVector operands; + M::FunctionType type; + llvm::SmallVector operands; if (parser.parseOperandList(operands, - mlir::OpAsmParser::Delimiter::None) || + M::OpAsmParser::Delimiter::None) || parser.parseColonType(type) || parser.resolveOperands(operands, type.getInputs(), parser.getNameLoc(), result.operands) || parser.addTypesToList(type.getResults(), result.types)) - return mlir::failure(); - return mlir::success(); + return M::failure(); + return M::success(); }]; let printer = [{ @@ -1160,16 +1145,16 @@ def fir_ExtractValueOp : fir_OneResultOp<"extract_value", [NoSideEffect]>, }]; let parser = [{ - mlir::FunctionType type; - llvm::SmallVector operands; + M::FunctionType type; + llvm::SmallVector operands; if (parser.parseOperandList(operands, - mlir::OpAsmParser::Delimiter::None) || + M::OpAsmParser::Delimiter::None) || parser.parseColonType(type) || parser.resolveOperands(operands, type.getInputs(), parser.getNameLoc(), result.operands) || parser.addTypesToList(type.getResults(), result.types)) - return mlir::failure(); - return mlir::success(); + return M::failure(); + return M::success(); }]; let printer = [{ @@ -1199,20 +1184,28 @@ def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoSideEffect]> { let builders = [OpBuilder< "Builder *builder, OperationState &result, StringRef fieldName", [{ - result.addAttribute("name", builder->getStringAttr(fieldName)); + result.addAttribute("field_id", builder->getStringAttr(fieldName)); }] >]; let parser = [{ - mlir::StringAttr attr; - mlir::Type type; - if (parser.parseLParen() || - parser.parseAttribute(attr, "field_id", result.attributes) || - parser.parseRParen() || + if (parser.parseLParen()) + return M::failure(); + llvm::StringRef fieldName; + if (parser.parseOptionalKeyword(&fieldName)) { + M::StringAttr attr; + if (parser.parseAttribute(attr, "field_id", result.attributes)) + return M::failure(); + } else { + result.addAttribute("field_id", + parser.getBuilder().getStringAttr(fieldName)); + } + M::Type type; + if (parser.parseRParen() || parser.parseColonType(type) || parser.addTypeToList(type, result.types)) - return mlir::failure(); - return mlir::success(); + return M::failure(); + return M::success(); }]; let printer = [{ @@ -1236,16 +1229,16 @@ def fir_GenDimsOp : fir_OneResultOp<"gendims", [NoSideEffect]>, }]; let parser = [{ - mlir::FunctionType type; - llvm::SmallVector operands; + M::FunctionType type; + llvm::SmallVector operands; if (parser.parseOperandList(operands, - mlir::OpAsmParser::Delimiter::None) || + M::OpAsmParser::Delimiter::None) || parser.parseColonType(type) || parser.resolveOperands(operands, type.getInputs(), parser.getNameLoc(), result.operands) || parser.addTypesToList(type.getResults(), result.types)) - return mlir::failure(); - return mlir::success(); + return M::failure(); + return M::success(); }]; let printer = [{ @@ -1266,16 +1259,16 @@ def fir_InsertValueOp : fir_OneResultOp<"insert_value", [NoSideEffect]>, }]; let parser = [{ - mlir::FunctionType type; - llvm::SmallVector operands; + M::FunctionType type; + llvm::SmallVector operands; if (parser.parseOperandList(operands, - mlir::OpAsmParser::Delimiter::None) || + M::OpAsmParser::Delimiter::None) || parser.parseColonType(type) || parser.resolveOperands(operands, type.getInputs(), parser.getNameLoc(), result.operands) || parser.addTypesToList(type.getResults(), result.types)) - return mlir::failure(); - return mlir::success(); + return M::failure(); + return M::success(); }]; let printer = [{ @@ -1288,8 +1281,7 @@ def fir_InsertValueOp : fir_OneResultOp<"insert_value", [NoSideEffect]>, }]; } -def fir_LenParamIndexOp : - fir_SimpleOneResultOp<"len_param_index", [NoSideEffect]> { +def fir_LenParamIndexOp : fir_OneResultOp<"len_param_index", [NoSideEffect]> { let summary = "create a field index value from a LEN type parameter identifier"; @@ -1307,20 +1299,28 @@ def fir_LenParamIndexOp : let builders = [OpBuilder< "Builder *builder, OperationState &result, StringRef fieldName", [{ - result.addAttribute("name", builder->getStringAttr(fieldName)); + result.addAttribute("field_id", builder->getStringAttr(fieldName)); }] >]; let parser = [{ - mlir::StringAttr attr; - mlir::Type type; - if (parser.parseLParen() || - parser.parseAttribute(attr, "field_id", result.attributes) || - parser.parseRParen() || + if (parser.parseLParen()) + return M::failure(); + llvm::StringRef fieldName; + if (parser.parseOptionalKeyword(&fieldName)) { + M::StringAttr attr; + if (parser.parseAttribute(attr, "field_id", result.attributes)) + return M::failure(); + } else { + result.addAttribute("field_id", + parser.getBuilder().getStringAttr(fieldName)); + } + M::Type type; + if (parser.parseRParen() || parser.parseColonType(type) || parser.addTypeToList(type, result.types)) - return mlir::failure(); - return mlir::success(); + return M::failure(); + return M::success(); }]; let printer = [{ @@ -1350,6 +1350,7 @@ def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> { "mlir::Value *lowerBound, mlir::Value *upperBound," "llvm::ArrayRef step"> ]; + let parser = [{ return parseLoopOp(parser, result); }]; let printer = [{ p << getOperationName() << ' ' << *getInductionVar() << " = " @@ -1365,11 +1366,12 @@ def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> { /*printBlockTerminators=*/false); p.printOptionalAttrDict(getAttrs(), {"unordered", "step"}); }]; + let verifier = [{ auto step = optstep(); if (step.begin() != step.end()) { auto *s = (*step.begin())->getDefiningOp(); - if (auto cst = dyn_cast_or_null(s)) { + if (auto cst = dyn_cast_or_null(s)) { if (cst.getValue() == 0) return emitOpError("constant step operand must be nonzero"); } @@ -1381,10 +1383,9 @@ def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> { if (body->getNumArguments() != 1 || !body->getArgument(0)->getType().isIndex()) return emitOpError("expected body to have a single index argument for " - "the induction variable"); - return mlir::success(); + "the induction variable"); + return M::success(); }]; - let parser = [{ return parseLoopOp(parser, result); }]; let extraClassDeclaration = [{ mlir::Block *getBody() { return ®ion().front(); } @@ -1420,6 +1421,7 @@ def fir_WhereOp : fir_Op<"where", [ImplicitFirTerminator]> { OpBuilder<"Builder *builder, OperationState &result, " "Value *cond, bool withOtherRegion"> ]; + let parser = [{ return parseWhereOp(parser, result); }]; let printer = [{ p << getOperationName() << ' ' << *condition(); @@ -1443,9 +1445,8 @@ def fir_WhereOp : fir_Op<"where", [ImplicitFirTerminator]> { if (b.getNumArguments() != 0) return emitOpError("requires that child entry blocks have no args"); } - return mlir::success(); + return M::success(); }]; - let parser = [{ return parseWhereOp(parser, result); }]; let extraClassDeclaration = [{ mlir::OpBuilder getWhereBodyBuilder() { @@ -1475,7 +1476,7 @@ def fir_CallOp : fir_Op<"call", []>, let parser = [{ M::FunctionType calleeType; - M::StringAttr proc; + M::SymbolRefAttr proc; L::SmallVector operands; if (parser.parseAttribute(proc, "proc", result.attributes) || parser.parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || @@ -1563,9 +1564,16 @@ def fir_DispatchOp : fir_Op<"dispatch", []>, M::FunctionType calleeType; L::SmallVector operands; auto calleeLoc = parser.getNameLoc(); - M::StringAttr calleeAttr; - if (parser.parseAttribute(calleeAttr, "method", result.attributes) || - parser.parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || + llvm::StringRef calleeName; + if (parser.parseOptionalKeyword(&calleeName)) { + M::StringAttr calleeAttr; + if (parser.parseAttribute(calleeAttr, "method", result.attributes)) + return M::failure(); + } else { + result.addAttribute("method", + parser.getBuilder().getStringAttr(calleeName)); + } + if (parser.parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || parser.parseOptionalAttributeDict(result.attributes) || parser.parseColonType(calleeType) || parser.addTypesToList(calleeType.getResults(), result.types) || @@ -1574,8 +1582,9 @@ def fir_DispatchOp : fir_Op<"dispatch", []>, return M::failure(); return M::success(); }]; + let printer = [{ - p << getAttr("method") << '('; + p << getOperationName() << ' ' << getAttr("method") << '('; p.printOperand(object()); if (arg_operand_begin() != arg_operand_end()) { p << ", "; @@ -1583,8 +1592,8 @@ def fir_DispatchOp : fir_Op<"dispatch", []>, } p << ')'; p.printOptionalAttrDict(getAttrs(), {"method"}); - llvm::SmallVector resTy(getResultTypes()); - llvm::SmallVector argTy(getOperandTypes()); + llvm::SmallVector resTy(getResultTypes()); + llvm::SmallVector argTy(getOperandTypes()); p << " : " << M::FunctionType::get(argTy, resTy, getContext()); }]; let extraClassDeclaration = [{ @@ -1609,20 +1618,22 @@ def fir_AddrOfOp : fir_OneResultOp<"address_of", [NoSideEffect]> { let arguments = (ins SymbolRefAttr:$symbol); let parser = [{ - mlir::SymbolRefAttr attr; - mlir::Type type; + M::SymbolRefAttr attr; + M::Type type; if (parser.parseLParen() || parser.parseAttribute(attr, "symbol", result.attributes) || parser.parseRParen() || + parser.parseOptionalAttributeDict(result.attributes) || parser.parseColonType(type) || parser.addTypeToList(type, result.types)) - return mlir::failure(); - return mlir::success(); + return M::failure(); + return M::success(); }]; let printer = [{ - p << getOperationName() << " (" << getAttr("symbol") << ") : " - << getType(); + p << getOperationName() << " (" << getAttr("symbol") << ')'; + p.printOptionalAttrDict(getAttrs(), {"symbol"}); + p << " : " << getType(); }]; } @@ -1635,20 +1646,22 @@ def fir_ConvertOp : fir_OneResultOp<"convert", [NoSideEffect]>, }]; let parser = [{ - mlir::FunctionType type; - llvm::SmallVector operands; + M::FunctionType type; + llvm::SmallVector operands; if (parser.parseOperandList(operands, 1, - mlir::OpAsmParser::Delimiter::None) || + M::OpAsmParser::Delimiter::None) || + parser.parseOptionalAttributeDict(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(); + return M::failure(); + return M::success(); }]; let printer = [{ p << getOperationName() << ' '; p.printOperand(value()); + p.printOptionalAttrDict(getAttrs(), {}); p << " : "; p.printFunctionalType(getOperation()); }]; @@ -1664,16 +1677,16 @@ def fir_GenTypeDescOp : fir_OneResultOp<"gentypedesc", [NoSideEffect]> { }]; let parser = [{ - mlir::Type intype; + M::Type intype; if (parser.parseType(intype)) - return mlir::failure(); + return M::failure(); auto &builder = parser.getBuilder(); result.addAttribute("in_type", builder.getTypeAttr(intype)); - mlir::Type restype; + M::Type restype; if (parser.parseColonType(restype) || parser.addTypeToList(restype, result.types)) - return mlir::failure(); - return mlir::success(); + return M::failure(); + return M::success(); }]; let printer = [{ p << getOperationName() << ' ' << getAttr("in_type"); @@ -1693,14 +1706,14 @@ def fir_NoReassocOp : fir_OneResultOp<"no_reassoc", }]; let parser = [{ - mlir::Type type; - mlir::OpAsmParser::OperandType opnd; + M::Type type; + M::OpAsmParser::OperandType opnd; if (parser.parseOperand(opnd) || parser.parseColonType(type) || parser.resolveOperand(opnd, type, result.operands) || parser.addTypeToList(type, result.types)) - return mlir::failure(); - return mlir::success(); + return M::failure(); + return M::success(); }]; let printer = [{ p << getOperationName() << ' '; @@ -1721,11 +1734,19 @@ def fir_GlobalEntryOp : fir_Op<"global_entry", []>, let parser = [{ M::Type type; - M::StringAttr fieldAttr; M::OpAsmParser::OperandType constant; - if (parser.parseAttribute(fieldAttr, "field", result.attributes) || - parser.parseComma() || + llvm::StringRef fieldName; + if (parser.parseOptionalKeyword(&fieldName)) { + M::StringAttr fieldAttr; + if (parser.parseAttribute(fieldAttr, "field", result.attributes)) + return M::failure(); + } else { + result.addAttribute("field", + parser.getBuilder().getStringAttr(fieldName)); + } + if (parser.parseComma() || parser.parseOperand(constant) || + parser.parseOptionalAttributeDict(result.attributes) || parser.parseColonType(type) || parser.resolveOperand(constant, type, result.operands)) return M::failure(); @@ -1735,6 +1756,7 @@ def fir_GlobalEntryOp : fir_Op<"global_entry", []>, let printer = [{ p << getOperationName() << ' ' << getAttr("field") << ", "; p.printOperand(constant()); + p.printOptionalAttrDict(getAttrs(), {"in_type", "field"}); p << " : " << getAttr("in_type"); }]; } @@ -1753,9 +1775,16 @@ def fir_DTEntryOp : fir_Op<"dt_entry", []>, let parser = [{ M::SymbolRefAttr calleeAttr; - M::StringAttr methodAttr; - if (parser.parseAttribute(methodAttr, "method", result.attributes) || - parser.parseComma() || + llvm::StringRef methodName; + if (parser.parseOptionalKeyword(&methodName)) { + M::StringAttr methodAttr; + if (parser.parseAttribute(methodAttr, "method", result.attributes)) + return M::failure(); + } else { + result.addAttribute("method", + parser.getBuilder().getStringAttr(methodName)); + } + if (parser.parseComma() || parser.parseAttribute(calleeAttr, "proc", result.attributes)) return M::failure(); return M::success(); diff --git a/include/fir/Tilikum/CMakeLists.txt b/include/fir/Tilikum/CMakeLists.txt deleted file mode 100644 index ecde2a633b7c..000000000000 --- a/include/fir/Tilikum/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - diff --git a/include/fir/Tilikum/LLVMConverter.h b/include/fir/Tilikum/LLVMConverter.h index d760834edfc3..06702c770c06 100644 --- a/include/fir/Tilikum/LLVMConverter.h +++ b/include/fir/Tilikum/LLVMConverter.h @@ -32,6 +32,6 @@ std::unique_ptr createStdToLLVMPass(); /// Convert the LLVM IR dialect to LLVM-IR proper std::unique_ptr createLLVMDialectToLLVMPass(); -} // fir +} // namespace fir -#endif // FIR_LLVM_CONVERTER_H +#endif // FIR_LLVM_CONVERTER_H diff --git a/include/fir/Tilikum/StdConverter.h b/include/fir/Tilikum/StdConverter.h index 411cb42df268..80ef0aca2615 100644 --- a/include/fir/Tilikum/StdConverter.h +++ b/include/fir/Tilikum/StdConverter.h @@ -26,6 +26,6 @@ namespace fir { /// Convert FIR to the standard dialect std::unique_ptr createFIRToStdPass(); -} // fir +} // namespace fir -#endif // FIR_STD_CONVERSION_H +#endif // FIR_STD_CONVERSION_H diff --git a/include/fir/Transforms/CMakeLists.txt b/include/fir/Transforms/CMakeLists.txt deleted file mode 100644 index ecde2a633b7c..000000000000 --- a/include/fir/Transforms/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - diff --git a/include/fir/Type.h b/include/fir/Type.h index 0f423f4dbaf5..4f684b848936 100644 --- a/include/fir/Type.h +++ b/include/fir/Type.h @@ -15,7 +15,9 @@ #ifndef FIR_TYPE_H #define FIR_TYPE_H +#include "mlir/IR/Attributes.h" #include "mlir/IR/Types.h" +#include "llvm/ADT/Optional.h" namespace llvm { class StringRef; @@ -138,9 +140,15 @@ class BoxType : public mlir::Type::TypeBase { public: using Base::Base; - static BoxType get(mlir::Type eleTy); + 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(llvm::Optional, + mlir::MLIRContext *ctx, mlir::Type eleTy, + mlir::AffineMapAttr map); }; class BoxCharType : public mlir::Type::TypeBase loc, + mlir::MLIRContext *context, mlir::Type eleTy); }; class DimsType : public mlir::Type::TypeBase loc, + mlir::MLIRContext *context, mlir::Type eleTy); }; class PointerType : public mlir::Type::TypeBase loc, + mlir::MLIRContext *context, mlir::Type eleTy); }; class ReferenceType @@ -209,6 +229,10 @@ class ReferenceType static bool kindof(unsigned kind) { return kind == TypeKind::FIR_REFERENCE; } mlir::Type getEleTy() const; + + static mlir::LogicalResult + verifyConstructionInvariants(llvm::Optional loc, + mlir::MLIRContext *context, mlir::Type eleTy); }; class SequenceType : public mlir::Type::TypeBase; using Bounds = std::vector; - struct Shape { - bool known; - Bounds bounds; - Shape() : known(false) {} - Shape(const Bounds &b) : known(true), bounds(b) {} - }; + using Shape = llvm::Optional; mlir::Type getEleTy() const; Shape getShape() const; + mlir::AffineMapAttr getLayoutMap() const; - static SequenceType get(const Shape &shape, mlir::Type elementType); + static SequenceType get(const Shape &shape, mlir::Type elementType, + mlir::AffineMapAttr map = {}); static bool kindof(unsigned kind) { return kind == TypeKind::FIR_SEQUENCE; } + static mlir::LogicalResult + verifyConstructionInvariants(llvm::Optional loc, + mlir::MLIRContext *context, const Shape &shape, + mlir::Type eleTy, mlir::AffineMapAttr map); }; bool operator==(const SequenceType::Shape &, const SequenceType::Shape &); @@ -247,6 +268,10 @@ class TypeDescType : public mlir::Type::TypeBase loc, + mlir::MLIRContext *context, mlir::Type ofType); }; // Derived types @@ -267,6 +292,12 @@ class RecordType : public mlir::Type::TypeBase typeList = {}); constexpr static bool kindof(unsigned kind) { return kind == getId(); } constexpr static unsigned getId() { return TypeKind::FIR_DERIVED; } + + static mlir::LogicalResult + verifyConstructionInvariants(llvm::Optional loc, + mlir::MLIRContext *context, llvm::StringRef name, + llvm::ArrayRef lenPList, + llvm::ArrayRef typeList); }; mlir::Type parseFirType(FIROpsDialect *dialect, llvm::StringRef rawData, @@ -274,4 +305,10 @@ mlir::Type parseFirType(FIROpsDialect *dialect, llvm::StringRef rawData, } // namespace fir +namespace llvm { +inline llvm::hash_code hash_value(const Optional &ext) { + return fir::hash_value(ext); +} +} // namespace llvm + #endif // FIR_TYPE_H diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b20b886fd409..776b6a11f8ff 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -6,6 +6,12 @@ # #===------------------------------------------------------------------------===# +if ((CMAKE_CXX_COMPILER_ID MATCHES "Clang")) + if (APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-string-conversion -Wno-covered-switch-default") + endif() +endif() + add_subdirectory(burnside) add_subdirectory(common) add_subdirectory(decimal) diff --git a/lib/burnside/CMakeLists.txt b/lib/burnside/CMakeLists.txt index 041a14f246f1..019fcadb8866 100644 --- a/lib/burnside/CMakeLists.txt +++ b/lib/burnside/CMakeLists.txt @@ -17,7 +17,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") add_library(FortranBurnside bridge.cc builder.cc - canonicalize.cc + convert-expr.cc fe-helper.cc flattened.cc runtime.cc diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 0cb991363f41..ebee0dd5224e 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -14,25 +14,18 @@ #include "bridge.h" #include "builder.h" -#include "canonicalize.h" +#include "convert-expr.h" #include "fe-helper.h" #include "fir/Dialect.h" #include "fir/FIROps.h" #include "fir/Type.h" #include "flattened.h" #include "runtime.h" -#include "../evaluate/expression.h" #include "../parser/parse-tree-visitor.h" #include "../semantics/tools.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/raw_ostream.h" -#include "mlir/Dialect/AffineOps/AffineOps.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/StandardOps/Ops.h" #include "mlir/IR/Builders.h" -#include "mlir/IR/Identifier.h" -#include "mlir/IR/Module.h" #include "mlir/Parser.h" #include "mlir/Target/LLVMIR.h" @@ -57,9 +50,11 @@ constexpr bool isStopStmt(Pa::StopStmt::Kind kind) { return kind == Pa::StopStmt::Kind::Stop; } +constexpr bool firLoopOp{false}; + /// Converter from Fortran to FIR -class MLIRConverter { - using LabelMapType = std::map; +class FIRConverter { + using LabelMapType = std::map; using Closure = std::function; struct DoBoundsInfo { @@ -71,24 +66,26 @@ class MLIRConverter { }; M::MLIRContext &mlirContext; - M::OwningModuleRef module_; - std::unique_ptr builder_; - LabelMapType blockMap_; // map from flattened labels to MLIR blocks + const Pa::CookedSource *cooked; + M::ModuleOp &module; + std::unique_ptr builder; + LabelMapType blockMap; // map from flattened labels to MLIR blocks std::list edgeQ; std::map doMap; SymMap symbolMap; - Pa::CharBlock lastKnownPos_; + Pa::CharBlock lastKnownPos; bool noInsPt{false}; - inline M::OpBuilder &build() { return *builder_.get(); } - inline M::ModuleOp getMod() { return module_.get(); } - inline LabelMapType &blkMap() { return blockMap_; } + inline M::OpBuilder &build() { return *builder.get(); } + inline M::ModuleOp &getMod() { return module; } + inline LabelMapType &blkMap() { return blockMap; } + void setCurrentPos(const Pa::CharBlock &pos) { lastKnownPos = pos; } /// Convert a parser CharBlock to a Location M::Location toLocation(const Pa::CharBlock &cb) { - return parserPosToLoc(mlirContext, cb); + return parserPosToLoc(mlirContext, cooked, cb); } - M::Location toLocation() { return toLocation(lastKnownPos_); } + M::Location toLocation() { return toLocation(lastKnownPos); } /// Construct the type of an Expr
expression M::Type exprType(const SomeExpr *expr) { @@ -122,7 +119,7 @@ class MLIRConverter { return createTemporary(toLocation(), build(), symbolMap, type, symbol); } - M::FuncOp genFunctionMLIR(llvm::StringRef callee, M::FunctionType funcTy) { + M::FuncOp genFunctionFIR(llvm::StringRef callee, M::FunctionType funcTy) { if (auto func{getNamedFunction(callee)}) { return func; } @@ -130,13 +127,13 @@ class MLIRConverter { } M::FuncOp genRuntimeFunction(RuntimeEntryCode rec, int kind) { - return genFunctionMLIR( + return genFunctionFIR( getRuntimeEntryName(rec), getRuntimeEntryType(rec, mlirContext, kind)); } template DoBoundsInfo *getBoundsInfo(const T &linearOp) { auto &st{std::get>(linearOp.v->t)}; - lastKnownPos_ = st.source; + setCurrentPos(st.source); auto *s{&st.statement}; auto iter{doMap.find(s)}; if (iter != doMap.end()) { @@ -178,10 +175,10 @@ class MLIRConverter { return build().create(lhs->getLoc(), lhs, rhs); } - void genMLIR(AnalysisData &ad, std::list &operations); + void genFIR(AnalysisData &ad, std::list &operations); // Control flow destination - void genMLIR(bool lastWasLabel, const Fl::LabelOp &op) { + void genFIR(bool lastWasLabel, const Fl::LabelOp &op) { if (lastWasLabel) { blkMap().insert({op.get(), build().getInsertionBlock()}); } else { @@ -197,14 +194,14 @@ class MLIRConverter { } // Goto statements - void genMLIR(const Fl::GotoOp &op) { + void genFIR(const Fl::GotoOp &op) { auto iter{blkMap().find(op.target)}; if (iter != blkMap().end()) { build().create(toLocation(), iter->second); } else { using namespace std::placeholders; edgeQ.emplace_back(std::bind( - [](M::OpBuilder *builder, M::Block *block, Fl::LabelRef dest, + [](M::OpBuilder *builder, M::Block *block, Fl::LabelMention dest, M::Location location, const LabelMapType &map) { builder->setInsertionPointToEnd(block); assert(map.find(dest) != map.end() && "no destination"); @@ -214,33 +211,33 @@ class MLIRConverter { } noInsPt = true; } - void genMLIR(const Fl::ReturnOp &op) { - std::visit([&](const auto *stmt) { genMLIR(*stmt); }, op.u); + void genFIR(const Fl::ReturnOp &op) { + std::visit([&](const auto *stmt) { genFIR(*stmt); }, op.u); noInsPt = true; } - void genMLIR(const Fl::ConditionalGotoOp &op) { + void genFIR(const Fl::ConditionalGotoOp &op) { std::visit( - [&](const auto *stmt) { genMLIR(*stmt, op.trueLabel, op.falseLabel); }, + [&](const auto *stmt) { genFIR(*stmt, op.trueLabel, op.falseLabel); }, op.u); noInsPt = true; } - void genMLIR(const Fl::SwitchIOOp &op); + void genFIR(const Fl::SwitchIOOp &op); // CALL with alt-return value returned - void genMLIR(const Fl::SwitchOp &op, const Pa::CallStmt &stmt) { + void genFIR(const Fl::SwitchOp &op, const Pa::CallStmt &stmt) { auto loc{toLocation(op.source)}; // FIXME (void)loc; } - void genMLIR(const Fl::SwitchOp &op, const Pa::ComputedGotoStmt &stmt) { + void genFIR(const Fl::SwitchOp &op, const Pa::ComputedGotoStmt &stmt) { auto loc{toLocation(op.source)}; auto *exp{Se::GetExpr(std::get(stmt.t))}; auto *e1{createFIRExpr(loc, exp)}; // FIXME (void)e1; } - void genMLIR(const Fl::SwitchOp &op, const Pa::ArithmeticIfStmt &stmt) { + void genFIR(const Fl::SwitchOp &op, const Pa::ArithmeticIfStmt &stmt) { auto loc{toLocation(op.source)}; auto *exp{Se::GetExpr(std::get(stmt.t))}; auto *e1{createFIRExpr(loc, exp)}; @@ -250,15 +247,15 @@ class MLIRConverter { M::Value *fromCaseValue(const M::Location &locs, const Pa::CaseValue &val) { return createFIRExpr(locs, Se::GetExpr(val)); } - void genMLIR(const Fl::SwitchOp &op, const Pa::CaseConstruct &stmt); - void genMLIR(const Fl::SwitchOp &op, const Pa::SelectRankConstruct &stmt); - void genMLIR(const Fl::SwitchOp &op, const Pa::SelectTypeConstruct &stmt); - void genMLIR(const Fl::SwitchOp &op) { - std::visit([&](auto *construct) { genMLIR(op, *construct); }, op.u); + void genFIR(const Fl::SwitchOp &op, const Pa::CaseConstruct &stmt); + void genFIR(const Fl::SwitchOp &op, const Pa::SelectRankConstruct &stmt); + void genFIR(const Fl::SwitchOp &op, const Pa::SelectTypeConstruct &stmt); + void genFIR(const Fl::SwitchOp &op) { + std::visit([&](auto *construct) { genFIR(op, *construct); }, op.u); noInsPt = true; } - void genMLIR(AnalysisData &ad, const Fl::ActionOp &op); + void genFIR(AnalysisData &ad, const Fl::ActionOp &op); void pushDoContext(const Pa::NonLabelDoStmt *doStmt, M::Value *doVar = nullptr, M::Value *counter = nullptr, @@ -266,16 +263,23 @@ class MLIRConverter { doMap.emplace(doStmt, DoBoundsInfo{doVar, counter, stepExpr}); } - void genLoopEnterMLIR(const Pa::LoopControl::Bounds &bounds, + void genLoopEnterFIR(const Pa::LoopControl::Bounds &bounds, const Pa::NonLabelDoStmt *stmt, const Pa::CharBlock &source) { auto loc{toLocation(source)}; + // evaluate e1, e2 [, e3] ... + auto *e1{createFIRExpr(loc, Se::GetExpr(bounds.lower))}; + auto *e2{createFIRExpr(loc, Se::GetExpr(bounds.upper))}; + if (firLoopOp) { + std::vector step; + if (bounds.step.has_value()) + step.push_back(createFIRExpr(loc, Se::GetExpr(bounds.step))); + auto loopOp{build().create(loc, e1, e2, step)}; + auto *block = createBlock(&build(), &loopOp.getOperation()->getRegion(0)); + block->addArgument(M::IndexType::get(build().getContext())); + return; + } auto *nameExpr{bounds.name.thing.symbol}; auto *name{createTemp(getDefaultIntegerType(), nameExpr)}; - // evaluate e1, e2 [, e3] ... - auto *lowerExpr{Se::GetExpr(bounds.lower)}; - auto *e1{createFIRExpr(loc, lowerExpr)}; - auto *upperExpr{Se::GetExpr(bounds.upper)}; - auto *e2{createFIRExpr(loc, upperExpr)}; M::Value *e3; if (bounds.step.has_value()) { auto *stepExpr{Se::GetExpr(bounds.step)}; @@ -298,40 +302,47 @@ class MLIRConverter { pushDoContext(stmt, name, tripCounter, e3); } - void genLoopEnterMLIR(const Pa::ScalarLogicalExpr &logicalExpr, + void genLoopEnterFIR(const Pa::ScalarLogicalExpr &logicalExpr, const Pa::NonLabelDoStmt *stmt, const Pa::CharBlock &source) { // See 11.1.7.4.1, para. 2 // See BuildLoopLatchExpression() pushDoContext(stmt); } - void genLoopEnterMLIR(const Pa::LoopControl::Concurrent &concurrent, + + void genLoopEnterFIR(const Pa::LoopControl::Concurrent &concurrent, const Pa::NonLabelDoStmt *stmt, const Pa::CharBlock &source) { // See 11.1.7.4.2 // FIXME } - void genEnterMLIR(const Pa::DoConstruct &construct) { + + void genEnterFIR(const Pa::DoConstruct &construct) { auto &stmt{std::get>(construct.t)}; - lastKnownPos_ = stmt.source; + setCurrentPos(stmt.source); const Pa::NonLabelDoStmt &ss{stmt.statement}; auto &ctrl{std::get>(ss.t)}; if (ctrl.has_value()) { - std::visit([&](const auto &x) { genLoopEnterMLIR(x, &ss, stmt.source); }, + std::visit([&](const auto &x) { genLoopEnterFIR(x, &ss, stmt.source); }, ctrl->u); } else { // loop forever (See 11.1.7.4.1, para. 2) pushDoContext(&ss); } } - template void genEnterMLIR(const A &construct) { - // FIXME: add other genEnterMLIR() members + template void genEnterFIR(const A &construct) { + // FIXME: add other genEnterFIR() members } - void genMLIR(const Fl::BeginOp &op) { - std::visit([&](auto *construct) { genEnterMLIR(*construct); }, op.u); + void genFIR(const Fl::BeginOp &op) { + std::visit([&](auto *construct) { genEnterFIR(*construct); }, op.u); } - void genExitMLIR(const Pa::DoConstruct &construct) { + void genExitFIR(const Pa::DoConstruct &construct) { + if (firLoopOp) { + build().setInsertionPointAfter( + build().getBlock()->getParent()->getParentOp()); + return; + } auto &stmt{std::get>(construct.t)}; - lastKnownPos_ = stmt.source; + setCurrentPos(stmt.source); const Pa::NonLabelDoStmt &ss{stmt.statement}; auto &ctrl{std::get>(ss.t)}; if (ctrl.has_value() && @@ -340,13 +351,18 @@ class MLIRConverter { } noInsPt = true; // backedge already processed } - void genMLIR(const Fl::EndOp &op) { + + void genFIR(const Fl::EndOp &op) { if (auto *construct{std::get_if(&op.u)}) - genExitMLIR(**construct); + genExitFIR(**construct); } - void genMLIR(AnalysisData &ad, const Fl::IndirectGotoOp &op); - void genMLIR(const Fl::DoIncrementOp &op) { + void genFIR(AnalysisData &ad, const Fl::IndirectGotoOp &op); + + void genFIR(const Fl::DoIncrementOp &op) { + if (firLoopOp) { + return; + } auto *info{getBoundsInfo(op)}; if (info->doVar && info->stepExpr) { // add: do_var = do_var + e3 @@ -356,8 +372,8 @@ class MLIRConverter { load.getLoc(), load.getResult(), info->stepExpr)}; build().create(load.getLoc(), incremented, info->doVar); // add: counter-- - auto loadCtr{build().create( - info->counter->getLoc(), info->counter)}; + auto loadCtr{ + build().create(info->counter->getLoc(), info->counter)}; auto one{build().create( loadCtr.getLoc(), build().getIntegerAttr(loadCtr.getType(), 1))}; auto decremented{build().create( @@ -366,12 +382,16 @@ class MLIRConverter { loadCtr.getLoc(), decremented, info->counter); } } - void genMLIR(const Fl::DoCompareOp &op) { + + void genFIR(const Fl::DoCompareOp &op) { + if (firLoopOp) { + return; + } auto *info{getBoundsInfo(op)}; if (info->doVar && info->stepExpr) { // add: cond = counter > 0 (signed) - auto load{build().create( - info->counter->getLoc(), info->counter)}; + auto load{ + build().create(info->counter->getLoc(), info->counter)}; auto zero{build().create( load.getLoc(), build().getIntegerAttr(load.getType(), 0))}; auto cond{build().create( @@ -379,16 +399,17 @@ class MLIRConverter { info->condition = cond; } } - void genMLIR(const Pa::FailImageStmt &stmt) { + + void genFIR(const Pa::FailImageStmt &stmt) { auto callee{genRuntimeFunction(FIRT_FAIL_IMAGE, 0)}; llvm::SmallVector operands; // FAIL IMAGE has no args build().create(toLocation(), callee, operands); build().create(toLocation()); } - void genMLIR(const Pa::ReturnStmt &stmt) { + void genFIR(const Pa::ReturnStmt &stmt) { build().create(toLocation()); // FIXME: argument(s)? } - void genMLIR(const Pa::StopStmt &stmt) { + void genFIR(const Pa::StopStmt &stmt) { auto callee{genRuntimeFunction( isStopStmt(std::get(stmt.t)) ? FIRT_STOP : FIRT_ERROR_STOP, @@ -401,26 +422,26 @@ class MLIRConverter { // Conditional branch-like statements template - void genMLIR( - const A &tuple, Fl::LabelRef trueLabel, Fl::LabelRef falseLabel) { + void genFIR( + const A &tuple, Fl::LabelMention trueLabel, Fl::LabelMention falseLabel) { auto *exprRef{Se::GetExpr(std::get(tuple))}; assert(exprRef && "condition expression missing"); auto *cond{createFIRExpr(toLocation(), exprRef)}; genCondBranch(cond, trueLabel, falseLabel); } - void genMLIR(const Pa::Statement &stmt, - Fl::LabelRef trueLabel, Fl::LabelRef falseLabel) { - lastKnownPos_ = stmt.source; - genMLIR(stmt.statement.t, trueLabel, falseLabel); + void genFIR(const Pa::Statement &stmt, + Fl::LabelMention trueLabel, Fl::LabelMention falseLabel) { + setCurrentPos(stmt.source); + genFIR(stmt.statement.t, trueLabel, falseLabel); } - void genMLIR(const Pa::Statement &stmt, - Fl::LabelRef trueLabel, Fl::LabelRef falseLabel) { - lastKnownPos_ = stmt.source; - genMLIR(stmt.statement.t, trueLabel, falseLabel); + void genFIR(const Pa::Statement &stmt, + Fl::LabelMention trueLabel, Fl::LabelMention falseLabel) { + setCurrentPos(stmt.source); + genFIR(stmt.statement.t, trueLabel, falseLabel); } - void genMLIR( - const Pa::IfStmt &stmt, Fl::LabelRef trueLabel, Fl::LabelRef falseLabel) { - genMLIR(stmt.t, trueLabel, falseLabel); + void genFIR(const Pa::IfStmt &stmt, Fl::LabelMention trueLabel, + Fl::LabelMention falseLabel) { + genFIR(stmt.t, trueLabel, falseLabel); } M::Value *getTrueConstant() { @@ -429,29 +450,40 @@ class MLIRConverter { } // Conditional branch to enter loop body or exit - void genMLIR(const Pa::Statement &stmt, - Fl::LabelRef trueLabel, Fl::LabelRef falseLabel) { - lastKnownPos_ = stmt.source; + void genFIR(const Pa::Statement &stmt, + Fl::LabelMention trueLabel, Fl::LabelMention falseLabel) { + setCurrentPos(stmt.source); auto &loopCtrl{std::get>(stmt.statement.t)}; M::Value *condition{nullptr}; + bool exitNow{false}; if (loopCtrl.has_value()) { - std::visit(Co::visitors{ - [&](const parser::LoopControl::Bounds &) { - auto iter{doMap.find(&stmt.statement)}; - assert(iter != doMap.end()); - condition = iter->second.condition->getResult(0); - }, - [&](const parser::ScalarLogicalExpr &logical) { - auto loc{toLocation(stmt.source)}; - auto *exp{Se::GetExpr(logical)}; - condition = createFIRExpr(loc, exp); - }, - [&](const parser::LoopControl::Concurrent &concurrent) { - // FIXME: incorrectly lowering DO CONCURRENT - condition = getTrueConstant(); - }, - }, + exitNow = std::visit( + Co::visitors{ + [&](const parser::LoopControl::Bounds &) { + if (firLoopOp) { + return true; + } + auto iter{doMap.find(&stmt.statement)}; + assert(iter != doMap.end()); + condition = iter->second.condition->getResult(0); + return false; + }, + [&](const parser::ScalarLogicalExpr &logical) { + auto loc{toLocation(stmt.source)}; + auto *exp{Se::GetExpr(logical)}; + condition = createFIRExpr(loc, exp); + return false; + }, + [&](const parser::LoopControl::Concurrent &concurrent) { + // FIXME: incorrectly lowering DO CONCURRENT + condition = getTrueConstant(); + return false; + }, + }, loopCtrl->u); + if (firLoopOp && exitNow) { + return; + } } else { condition = getTrueConstant(); } @@ -460,49 +492,49 @@ class MLIRConverter { } // Action statements - void genMLIR(const Pa::AllocateStmt &stmt); - void genMLIR(const Pa::AssignmentStmt &stmt) { + void genFIR(const Pa::AllocateStmt &stmt); + void genFIR(const Pa::AssignmentStmt &stmt) { auto *rhs{Se::GetExpr(std::get(stmt.t))}; auto *lhs{Se::GetExpr(std::get(stmt.t))}; auto loc{toLocation()}; build().create( loc, createFIRExpr(loc, rhs), createFIRAddr(loc, lhs)); } - void genMLIR(const Pa::BackspaceStmt &stmt); - void genMLIR(const Pa::CallStmt &stmt); - void genMLIR(const Pa::CloseStmt &stmt); - void genMLIR(const Pa::DeallocateStmt &stmt); - void genMLIR(const Pa::EndfileStmt &stmt); - void genMLIR(const Pa::EventPostStmt &stmt); - void genMLIR(const Pa::EventWaitStmt &stmt); - void genMLIR(const Pa::FlushStmt &stmt); - void genMLIR(const Pa::FormTeamStmt &stmt); - void genMLIR(const Pa::InquireStmt &stmt); - void genMLIR(const Pa::LockStmt &stmt); - void genMLIR(const Pa::NullifyStmt &stmt); - void genMLIR(const Pa::OpenStmt &stmt); - void genMLIR(const Pa::PointerAssignmentStmt &stmt); - void genMLIR(const Pa::PrintStmt &stmt); - void genMLIR(const Pa::ReadStmt &stmt); - void genMLIR(const Pa::RewindStmt &stmt); - void genMLIR(const Pa::SyncAllStmt &stmt); - void genMLIR(const Pa::SyncImagesStmt &stmt); - void genMLIR(const Pa::SyncMemoryStmt &stmt); - void genMLIR(const Pa::SyncTeamStmt &stmt); - void genMLIR(const Pa::UnlockStmt &stmt); - void genMLIR(const Pa::WaitStmt &stmt); - void genMLIR(const Pa::WhereStmt &stmt); - void genMLIR(const Pa::WriteStmt &stmt); - void genMLIR(const Pa::ForallStmt &stmt); - void genMLIR(AnalysisData &ad, const Pa::AssignStmt &stmt); - void genMLIR(const Pa::PauseStmt &stmt); + void genFIR(const Pa::BackspaceStmt &stmt); + void genFIR(const Pa::CallStmt &stmt); + void genFIR(const Pa::CloseStmt &stmt); + void genFIR(const Pa::DeallocateStmt &stmt); + void genFIR(const Pa::EndfileStmt &stmt); + void genFIR(const Pa::EventPostStmt &stmt); + void genFIR(const Pa::EventWaitStmt &stmt); + void genFIR(const Pa::FlushStmt &stmt); + void genFIR(const Pa::FormTeamStmt &stmt); + void genFIR(const Pa::InquireStmt &stmt); + void genFIR(const Pa::LockStmt &stmt); + void genFIR(const Pa::NullifyStmt &stmt); + void genFIR(const Pa::OpenStmt &stmt); + void genFIR(const Pa::PointerAssignmentStmt &stmt); + void genFIR(const Pa::PrintStmt &stmt); + void genFIR(const Pa::ReadStmt &stmt); + void genFIR(const Pa::RewindStmt &stmt); + void genFIR(const Pa::SyncAllStmt &stmt); + void genFIR(const Pa::SyncImagesStmt &stmt); + void genFIR(const Pa::SyncMemoryStmt &stmt); + void genFIR(const Pa::SyncTeamStmt &stmt); + void genFIR(const Pa::UnlockStmt &stmt); + void genFIR(const Pa::WaitStmt &stmt); + void genFIR(const Pa::WhereStmt &stmt); + void genFIR(const Pa::WriteStmt &stmt); + void genFIR(const Pa::ForallStmt &stmt); + void genFIR(AnalysisData &ad, const Pa::AssignStmt &stmt); + void genFIR(const Pa::PauseStmt &stmt); template void translateRoutine( - const A &routine, const std::string &name, const Se::Symbol *funcSym); + const A &routine, llvm::StringRef name, const Se::Symbol *funcSym); void genCondBranch( - M::Value *cond, Fl::LabelRef trueBlock, Fl::LabelRef falseBlock) { + M::Value *cond, Fl::LabelMention trueBlock, Fl::LabelMention falseBlock) { auto trueIter{blkMap().find(trueBlock)}; auto falseIter{blkMap().find(falseBlock)}; if (trueIter != blkMap().end() && falseIter != blkMap().end()) { @@ -513,7 +545,7 @@ class MLIRConverter { using namespace std::placeholders; edgeQ.emplace_back(std::bind( [](M::OpBuilder *builder, M::Block *block, M::Value *cnd, - Fl::LabelRef trueDest, Fl::LabelRef falseDest, + Fl::LabelMention trueDest, Fl::LabelMention falseDest, M::Location location, const LabelMapType &map) { llvm::SmallVector blk; builder->setInsertionPointToEnd(block); @@ -531,7 +563,7 @@ class MLIRConverter { template void genSwitchBranch(const M::Location &loc, M::Value *selector, std::list &&conditions, - const std::vector &labels) { + const std::vector &labels) { assert(conditions.size() == labels.size()); bool haveAllLabels{true}; std::size_t u{0}; @@ -559,7 +591,7 @@ class MLIRConverter { edgeQ.emplace_back(std::bind( [](M::OpBuilder *builder, M::Block *block, M::Value *sel, const std::list &conditions, - const std::vector &labels, M::Location location, + const std::vector &labels, M::Location location, const LabelMapType &map) { std::size_t u{0}; std::vector conds; @@ -588,11 +620,10 @@ class MLIRConverter { } public: - MLIRConverter(BurnsideBridge &bridge) - : mlirContext{bridge.getMLIRContext()}, module_{bridge.getModule()} {} - MLIRConverter() = delete; - - M::ModuleOp getModule() { return getMod(); } + FIRConverter(BurnsideBridge &bridge) + : mlirContext{bridge.getMLIRContext()}, cooked{bridge.getCookedSource()}, + module{bridge.getModule()} {} + FIRConverter() = delete; template constexpr bool Pre(const A &) { return true; } template constexpr void Post(const A &) {} @@ -603,19 +634,19 @@ class MLIRConverter { if (auto &ps{ std::get>>(mainp.t)}) { mainName = ps->statement.v.ToString(); - lastKnownPos_ = ps->source; + setCurrentPos(ps->source); } translateRoutine(mainp, mainName, nullptr); } void Post(const Pa::FunctionSubprogram &subp) { auto &stmt{std::get>(subp.t)}; - lastKnownPos_ = stmt.source; + setCurrentPos(stmt.source); auto &name{std::get(stmt.statement.t)}; translateRoutine(subp, name.ToString(), name.symbol); } void Post(const Pa::SubroutineSubprogram &subp) { auto &stmt{std::get>(subp.t)}; - lastKnownPos_ = stmt.source; + setCurrentPos(stmt.source); auto &name{std::get(stmt.statement.t)}; translateRoutine(subp, name.ToString(), name.symbol); } @@ -623,7 +654,7 @@ class MLIRConverter { /// SELECT CASE /// Build a switch-like structure for a SELECT CASE -void MLIRConverter::genMLIR( +void FIRConverter::genFIR( const Fl::SwitchOp &op, const Pa::CaseConstruct &stmt) { auto loc{toLocation(op.source)}; auto &cstm{std::get>(stmt.t)}; @@ -681,7 +712,7 @@ void MLIRConverter::genMLIR( /// SELECT RANK /// Build a switch-like structure for a SELECT RANK -void MLIRConverter::genMLIR( +void FIRConverter::genFIR( const Fl::SwitchOp &op, const Pa::SelectRankConstruct &stmt) { auto loc{toLocation(op.source)}; auto &rstm{std::get>(stmt.t)}; @@ -719,7 +750,7 @@ void MLIRConverter::genMLIR( /// SELECT TYPE /// Build a switch-like structure for a SELECT TYPE -void MLIRConverter::genMLIR( +void FIRConverter::genFIR( const Fl::SwitchOp &op, const Pa::SelectTypeConstruct &stmt) { auto loc{toLocation(op.source)}; auto &tstm{std::get>(stmt.t)}; @@ -758,43 +789,43 @@ void MLIRConverter::genMLIR( loc, e3.getResult(0), std::move(conds), op.refs); } -void MLIRConverter::genMLIR(const Fl::SwitchIOOp &op) {} +void FIRConverter::genFIR(const Fl::SwitchIOOp &op) {} -void MLIRConverter::genMLIR(const Pa::AllocateStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::BackspaceStmt &stmt) { +void FIRConverter::genFIR(const Pa::AllocateStmt &stmt) {} +void FIRConverter::genFIR(const Pa::BackspaceStmt &stmt) { // builder->create(stmt.v); } -void MLIRConverter::genMLIR(const Pa::CallStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::CloseStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::DeallocateStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::EndfileStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::EventPostStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::EventWaitStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::FlushStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::FormTeamStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::InquireStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::LockStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::NullifyStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::OpenStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::PointerAssignmentStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::PrintStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::ReadStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::RewindStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::SyncAllStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::SyncImagesStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::SyncMemoryStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::SyncTeamStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::UnlockStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::WaitStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::WhereStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::WriteStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::ForallStmt &stmt) {} -void MLIRConverter::genMLIR(AnalysisData &ad, const Pa::AssignStmt &stmt) {} -void MLIRConverter::genMLIR(const Pa::PauseStmt &stmt) {} +void FIRConverter::genFIR(const Pa::CallStmt &stmt) {} +void FIRConverter::genFIR(const Pa::CloseStmt &stmt) {} +void FIRConverter::genFIR(const Pa::DeallocateStmt &stmt) {} +void FIRConverter::genFIR(const Pa::EndfileStmt &stmt) {} +void FIRConverter::genFIR(const Pa::EventPostStmt &stmt) {} +void FIRConverter::genFIR(const Pa::EventWaitStmt &stmt) {} +void FIRConverter::genFIR(const Pa::FlushStmt &stmt) {} +void FIRConverter::genFIR(const Pa::FormTeamStmt &stmt) {} +void FIRConverter::genFIR(const Pa::InquireStmt &stmt) {} +void FIRConverter::genFIR(const Pa::LockStmt &stmt) {} +void FIRConverter::genFIR(const Pa::NullifyStmt &stmt) {} +void FIRConverter::genFIR(const Pa::OpenStmt &stmt) {} +void FIRConverter::genFIR(const Pa::PointerAssignmentStmt &stmt) {} +void FIRConverter::genFIR(const Pa::PrintStmt &stmt) {} +void FIRConverter::genFIR(const Pa::ReadStmt &stmt) {} +void FIRConverter::genFIR(const Pa::RewindStmt &stmt) {} +void FIRConverter::genFIR(const Pa::SyncAllStmt &stmt) {} +void FIRConverter::genFIR(const Pa::SyncImagesStmt &stmt) {} +void FIRConverter::genFIR(const Pa::SyncMemoryStmt &stmt) {} +void FIRConverter::genFIR(const Pa::SyncTeamStmt &stmt) {} +void FIRConverter::genFIR(const Pa::UnlockStmt &stmt) {} +void FIRConverter::genFIR(const Pa::WaitStmt &stmt) {} +void FIRConverter::genFIR(const Pa::WhereStmt &stmt) {} +void FIRConverter::genFIR(const Pa::WriteStmt &stmt) {} +void FIRConverter::genFIR(const Pa::ForallStmt &stmt) {} +void FIRConverter::genFIR(AnalysisData &ad, const Pa::AssignStmt &stmt) {} +void FIRConverter::genFIR(const Pa::PauseStmt &stmt) {} /// translate action statements -void MLIRConverter::genMLIR(AnalysisData &ad, const Fl::ActionOp &op) { - lastKnownPos_ = op.v->source; +void FIRConverter::genFIR(AnalysisData &ad, const Fl::ActionOp &op) { + setCurrentPos(op.v->source); std::visit( Co::visitors{ [](const Pa::ContinueStmt &) { assert(false); }, @@ -809,42 +840,42 @@ void MLIRConverter::genMLIR(AnalysisData &ad, const Fl::ActionOp &op) { [](const Co::Indirection &) { assert(false); }, [](const Co::Indirection &) { assert(false); }, [&](const Co::Indirection &assign) { - genMLIR(ad, assign.value()); + genFIR(ad, assign.value()); }, - [&](const auto &stmt) { genMLIR(stmt.value()); }, + [&](const auto &stmt) { genFIR(stmt.value()); }, }, op.v->statement.u); } -void MLIRConverter::genMLIR(AnalysisData &ad, const Fl::IndirectGotoOp &op) { +void FIRConverter::genFIR(AnalysisData &ad, const Fl::IndirectGotoOp &op) { // add or queue an igoto } -void MLIRConverter::genMLIR(AnalysisData &ad, std::list &operations) { +void FIRConverter::genFIR(AnalysisData &ad, std::list &operations) { bool lastWasLabel{false}; for (auto &op : operations) { std::visit(Co::visitors{ [&](const Fl::IndirectGotoOp &oper) { - genMLIR(ad, oper); + genFIR(ad, oper); lastWasLabel = false; }, [&](const Fl::ActionOp &oper) { noInsPt = false; - genMLIR(ad, oper); + genFIR(ad, oper); lastWasLabel = false; }, [&](const Fl::LabelOp &oper) { - genMLIR(lastWasLabel, oper); + genFIR(lastWasLabel, oper); lastWasLabel = true; }, [&](const Fl::BeginOp &oper) { noInsPt = false; - genMLIR(oper); + genFIR(oper); lastWasLabel = true; }, [&](const auto &oper) { noInsPt = false; - genMLIR(oper); + genFIR(oper); lastWasLabel = false; }, }, @@ -858,8 +889,8 @@ void MLIRConverter::genMLIR(AnalysisData &ad, std::list &operations) { /// Translate the routine to MLIR template -void MLIRConverter::translateRoutine( - const A &routine, const std::string &name, const Se::Symbol *funcSym) { +void FIRConverter::translateRoutine( + const A &routine, llvm::StringRef name, const Se::Symbol *funcSym) { M::FuncOp func{getNamedFunction(name)}; if (!func) { // get arguments and return type if any, otherwise just use empty vectors @@ -886,7 +917,7 @@ void MLIRConverter::translateRoutine( func = createFunction(getMod(), name, funcTy); } func.addEntryBlock(); - builder_ = std::make_unique(func); + builder = std::make_unique(func); build().setInsertionPointToStart(&func.front()); if (funcSym) { auto *entryBlock{&func.front()}; @@ -904,7 +935,7 @@ void MLIRConverter::translateRoutine( AnalysisData ad; std::list operations; CreateFlatIR(routine, operations, ad); - genMLIR(ad, operations); + genFIR(ad, operations); finalizeQueued(); } @@ -913,7 +944,7 @@ M::DialectRegistration FIROps; } // namespace void Br::crossBurnsideBridge(BurnsideBridge &bridge, const Pa::Program &prg) { - MLIRConverter converter{bridge}; + FIRConverter converter{bridge}; Walk(prg, converter); } @@ -922,25 +953,29 @@ std::unique_ptr Br::LLVMBridge(M::ModuleOp &module) { } void Br::BurnsideBridge::parseSourceFile(llvm::SourceMgr &srcMgr) { - module_ = M::parseSourceFile(srcMgr, context_.get()); + auto owningRef = M::parseSourceFile(srcMgr, context.get()); + module.reset(new M::ModuleOp(owningRef.get().getOperation())); + owningRef.release(); if (validModule()) { // symbols are added by ModuleManager ctor - manager_.reset(new M::ModuleManager(getModule())); + manager.reset(new M::ModuleManager(getModule())); } } Br::BurnsideBridge::BurnsideBridge( - const Co::IntrinsicTypeDefaultKinds &defaultKinds) - : defaultKinds_{defaultKinds} { - context_ = std::make_unique(); - module_ = M::OwningModuleRef{ - M::ModuleOp::create(M::UnknownLoc::get(context_.get()))}; - manager_ = std::make_unique(getModule()); + const Co::IntrinsicTypeDefaultKinds &defaultKinds, + const Pa::CookedSource *cooked) + : defaultKinds{defaultKinds}, cooked{cooked} { + context = std::make_unique(); + module = std::make_unique( + M::ModuleOp::create(M::UnknownLoc::get(context.get()))); + manager = std::make_unique(getModule()); } void Br::instantiateBurnsideBridge( - const Co::IntrinsicTypeDefaultKinds &defaultKinds) { - auto p{BurnsideBridge::create(defaultKinds)}; + const Co::IntrinsicTypeDefaultKinds &defaultKinds, + const Pa::CookedSource *cooked) { + auto p{BurnsideBridge::create(defaultKinds, cooked)}; bridgeInstance.swap(p); } diff --git a/lib/burnside/bridge.h b/lib/burnside/bridge.h index edd2b0b60376..f0d153f4913f 100644 --- a/lib/burnside/bridge.h +++ b/lib/burnside/bridge.h @@ -19,14 +19,17 @@ #include "mlir/IR/Module.h" #include -// Implement the burnside bridge from Fortran to MLIR -// https://github.com/tensorflow/mlir +/// Implement the burnside bridge from Fortran to +/// [MLIR](https://github.com/tensorflow/mlir). +/// +/// [Coding style](https://llvm.org/docs/CodingStandards.html) namespace Fortran::common { class IntrinsicTypeDefaultKinds; } namespace Fortran::parser { +class CookedSource; struct Program; } @@ -42,33 +45,37 @@ namespace Fortran::burnside { class BurnsideBridge { public: static std::unique_ptr create( - const common::IntrinsicTypeDefaultKinds &defaultKinds) { - BurnsideBridge *p = new BurnsideBridge{defaultKinds}; + const common::IntrinsicTypeDefaultKinds &defaultKinds, + const parser::CookedSource *cooked) { + BurnsideBridge *p = new BurnsideBridge{defaultKinds, cooked}; return std::unique_ptr{p}; } - mlir::MLIRContext &getMLIRContext() { return *context_.get(); } - mlir::ModuleManager &getManager() { return *manager_.get(); } - mlir::ModuleOp getModule() { return module_.get(); } + mlir::MLIRContext &getMLIRContext() { return *context.get(); } + mlir::ModuleManager &getManager() { return *manager.get(); } + mlir::ModuleOp &getModule() { return *module.get(); } void parseSourceFile(llvm::SourceMgr &); const common::IntrinsicTypeDefaultKinds &getDefaultKinds() { - return defaultKinds_; + return defaultKinds; } bool validModule() { return getModule(); } + const parser::CookedSource *getCookedSource() const { return cooked; } + private: - explicit BurnsideBridge( - const common::IntrinsicTypeDefaultKinds &defaultKinds); + explicit BurnsideBridge(const common::IntrinsicTypeDefaultKinds &defaultKinds, + const parser::CookedSource *cooked); BurnsideBridge() = delete; BurnsideBridge(const BurnsideBridge &) = delete; - const common::IntrinsicTypeDefaultKinds &defaultKinds_; - std::unique_ptr context_; - mlir::OwningModuleRef module_; - std::unique_ptr manager_; + const common::IntrinsicTypeDefaultKinds &defaultKinds; + const parser::CookedSource *cooked; + std::unique_ptr context; + std::unique_ptr module; + std::unique_ptr manager; }; /// Cross the bridge from the Fortran parse-tree, etc. to FIR+OpenMP+MLIR @@ -80,7 +87,8 @@ std::unique_ptr LLVMBridge(mlir::ModuleOp &module); /// instantiate the BURNSIDE bridge singleton void instantiateBurnsideBridge( - const common::IntrinsicTypeDefaultKinds &defaultKinds); + const common::IntrinsicTypeDefaultKinds &defaultKinds, + const parser::CookedSource *cooked = nullptr); /// access to the default kinds class (for MLIR bridge) const common::IntrinsicTypeDefaultKinds &getDefaultKinds(); diff --git a/lib/burnside/builder.cc b/lib/burnside/builder.cc index 67e37cd36ec3..00aec267309f 100644 --- a/lib/burnside/builder.cc +++ b/lib/burnside/builder.cc @@ -19,38 +19,34 @@ #include "mlir/IR/Module.h" #include "mlir/IR/Value.h" -namespace Br = Fortran::burnside; -namespace M = mlir; -namespace Se = Fortran::semantics; +namespace B = Fortran::burnside; using namespace Fortran; -using namespace Fortran::burnside; +using namespace B; // This will need to be extended to consider the type of what is being mangled -std::string Br::applyNameMangling(llvm::StringRef parserName) { +std::string B::applyNameMangling(llvm::StringRef parserName) { // FIXME: this is fake for now, add type info, etc. return "_Qp_"s + parserName.str(); } -M::FuncOp Br::createFunction( - M::ModuleOp module, const std::string &name, M::FunctionType funcTy) { - M::MLIRContext *ctxt{module.getContext()}; - auto func{M::FuncOp::create(dummyLoc(ctxt), name, funcTy)}; +mlir::FuncOp B::createFunction( + mlir::ModuleOp module, llvm::StringRef name, mlir::FunctionType funcTy) { + mlir::MLIRContext *ctxt{module.getContext()}; + auto func{mlir::FuncOp::create(dummyLoc(ctxt), name, funcTy)}; module.push_back(func); return func; } -M::FuncOp Br::getNamedFunction(llvm::StringRef name) { - return getBridge().getManager().lookupSymbol(name); +mlir::FuncOp B::getNamedFunction(llvm::StringRef name) { + return getBridge().getManager().lookupSymbol(name); } -// symbol map: {Symbol* -> Value*} - -void Br::SymMap::addSymbol(const Se::Symbol *symbol, M::Value *value) { - sMap.try_emplace(symbol, value); +void B::SymMap::addSymbol(const semantics::Symbol *symbol, mlir::Value *value) { + symbolMap.try_emplace(symbol, value); } -M::Value *Br::SymMap::lookupSymbol(const Se::Symbol *symbol) { - auto iter{sMap.find(symbol)}; - return (iter == sMap.end()) ? nullptr : iter->second; +mlir::Value *B::SymMap::lookupSymbol(const semantics::Symbol *symbol) { + auto iter{symbolMap.find(symbol)}; + return (iter == symbolMap.end()) ? nullptr : iter->second; } diff --git a/lib/burnside/common.h b/lib/burnside/common.h deleted file mode 100644 index 2c3720c612ec..000000000000 --- a/lib/burnside/common.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef FORTRAN_BURNSIDE_COMMON_H_ -#define FORTRAN_BURNSIDE_COMMON_H_ - -#include "../evaluate/expression.h" - -namespace Fortran::burnside { - -using Expression = evaluate::Expr; -using CallArguments = std::vector; - -enum InputOutputCallType { - InputOutputCallBackspace = 11, - InputOutputCallClose, - InputOutputCallEndfile, - InputOutputCallFlush, - InputOutputCallInquire, - InputOutputCallOpen, - InputOutputCallPrint, - InputOutputCallRead, - InputOutputCallRewind, - InputOutputCallWait, - InputOutputCallWrite, - InputOutputCallSIZE = InputOutputCallWrite - InputOutputCallBackspace + 1 -}; - -using IOCallArguments = CallArguments; - -enum RuntimeCallType { - RuntimeCallFailImage = 31, - RuntimeCallStop, - RuntimeCallPause, - RuntimeCallFormTeam, - RuntimeCallEventPost, - RuntimeCallEventWait, - RuntimeCallSyncAll, - RuntimeCallSyncImages, - RuntimeCallSyncMemory, - RuntimeCallSyncTeam, - RuntimeCallLock, - RuntimeCallUnlock, - RuntimeCallSIZE = RuntimeCallUnlock - RuntimeCallFailImage + 1 -}; - -using RuntimeCallArguments = CallArguments; - -} // Fortran::burnside - -#endif // FORTRAN_BURNSIDE_COMMON_H_ diff --git a/lib/burnside/canonicalize.cc b/lib/burnside/convert-expr.cc similarity index 99% rename from lib/burnside/canonicalize.cc rename to lib/burnside/convert-expr.cc index 91524fe6de19..e670d7603757 100644 --- a/lib/burnside/canonicalize.cc +++ b/lib/burnside/convert-expr.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "canonicalize.h" +#include "convert-expr.h" #include "builder.h" #include "fe-helper.h" #include "fir/Dialect.h" @@ -488,12 +488,12 @@ class ExprLowering { auto unwrapTy{arrTy.cast().getEleTy()}; auto seqTy{unwrapTy.cast()}; auto shape = seqTy.getShape(); - if (shape.known) { - if (dims < shape.bounds.size()) { + if (shape.hasValue()) { + if (dims < shape->size()) { fir::SequenceType::Bounds newBnds; // follow Fortran semantics and remove columns for (unsigned i = 0; i < dims; ++i) { - newBnds.push_back(shape.bounds[i]); + newBnds.push_back((*shape)[i]); } return fir::SequenceType::get({newBnds}, seqTy.getEleTy()); } @@ -607,7 +607,6 @@ M::Value *Br::createTemporary(M::Location loc, M::OpBuilder &builder, builder.setInsertionPointToStart(getEntryBlock(&builder)); fir::AllocaOp ae; assert(!type.dyn_cast() && "cannot be a reference"); - type = fir::ReferenceType::get(type); if (symbol) { ae = builder.create(loc, type, symbol->name().ToString()); symMap.addSymbol(symbol, ae); diff --git a/lib/burnside/canonicalize.h b/lib/burnside/convert-expr.h similarity index 86% rename from lib/burnside/canonicalize.h rename to lib/burnside/convert-expr.h index e8de6c068b08..a07ae02a5b08 100644 --- a/lib/burnside/canonicalize.h +++ b/lib/burnside/convert-expr.h @@ -12,11 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FORTRAN_BURNSIDE_CANONICALIZE_H_ -#define FORTRAN_BURNSIDE_CANONICALIZE_H_ +#ifndef FORTRAN_BURNSIDE_CONVERT_EXPR_H_ +#define FORTRAN_BURNSIDE_CONVERT_EXPR_H_ -// In the Fortran::burnside namespace, the code will default follow the -// LLVM/MLIR coding standards +/// [Coding style](https://llvm.org/docs/CodingStandards.html) namespace mlir { class Location; @@ -55,4 +54,4 @@ mlir::Value *createTemporary(mlir::Location loc, mlir::OpBuilder &builder, } // burnside } // Fortran -#endif // FORTRAN_BURNSIDE_CANONICALIZE_H_ +#endif // FORTRAN_BURNSIDE_CONVERT_EXPR_H_ diff --git a/lib/burnside/expression.cc b/lib/burnside/expression.cc deleted file mode 100644 index 367deb71c5fb..000000000000 --- a/lib/burnside/expression.cc +++ /dev/null @@ -1,405 +0,0 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "expression.h" -#include "builder.h" -#include "fe-helper.h" -#include "../semantics/expression.h" -#include "../semantics/symbol.h" -#include "../semantics/tools.h" -#include "../semantics/type.h" -#include "fir/Dialect.h" -#include "fir/Type.h" -#include "llvm/Support/raw_ostream.h" -#include "mlir/IR/Attributes.h" -#include "mlir/IR/Builders.h" -#include "mlir/IR/Module.h" -#include "mlir/IR/Operation.h" -#include "mlir/IR/StandardTypes.h" -#include "mlir/StandardOps/Ops.h" - -namespace Br = Fortran::burnside; -namespace Co = Fortran::common; -namespace Ev = Fortran::evaluate; -namespace M = mlir; -namespace Pa = Fortran::parser; -namespace Se = Fortran::semantics; - -using namespace Fortran; -using namespace Fortran::burnside; - -namespace { - -#undef TODO -#define TODO() assert(false) - -/// Collect the arguments and build the dictionary for FIR instructions with -/// embedded evaluate::Expr attributes. These arguments strictly conform to -/// data flow between Fortran expressions. -class TreeArgsBuilder { - M::OpBuilder *builder; - Args results; - Dict dictionary; - ExprType visited{ET_NONE}; - std::set keys; - SymMap &symbolMap; - - void addResult(M::Value *v, void *expr) { - const auto index{results.size()}; - results.push_back(v); - dictionary[index] = expr; - keys.insert(expr); - } - - void addResult(M::Value *v, const void *expr) { - addResult(v, const_cast(expr)); - } - - // FIXME: how do we map an evaluate::Expr to a source location? - M::Location dummyLoc() { return M::UnknownLoc::get(builder->getContext()); } - -public: - explicit TreeArgsBuilder(M::OpBuilder *builder, SymMap &map) - : builder{builder}, symbolMap{map} {} - - // FIXME: what we generate for a `Symbol` depends on the category of the - // symbol - void gen(const Se::Symbol *variable, M::Type ty, bool asAddr) { - if (keys.find(const_cast(variable)) == keys.end()) { - auto *addr = symbolMap.lookupSymbol(variable); - if (!addr) { - auto ip{builder->getInsertionBlock()}; - builder->setInsertionPointToStart(getEntryBlock(builder)); - addr = builder->create(dummyLoc(), ty); - symbolMap.addSymbol(variable, addr); - builder->setInsertionPointToEnd(ip); - } - if (asAddr) { - addResult(addr, variable); - } else { - llvm::SmallVector loadArg{addr}; - auto load{builder->create(dummyLoc(), loadArg, ty)}; - addResult(load.getResult(), variable); - } - } - visited = ET_Symbol; - } - - template void gen(const Ev::Designator &des, bool asAddr) { - std::visit(Co::visitors{ - [&](const Se::Symbol *x) { - auto *ctx{builder->getContext()}; - M::Type ty{translateDesignatorToFIRType(ctx, des)}; - gen(x, ty, asAddr); - }, - [&](const auto &x) { gen(x, asAddr); }, - }, - des.u); - } - - template - void gen(const Ev::Operation &op, bool asAddr) { - gen(op.left(), asAddr); - if constexpr (op.operands > 1) { - gen(op.right(), asAddr); - } - visited = ET_Operation; - } - - void gen(const Ev::DataRef &dref, bool asAddr) { - std::visit(Co::visitors{ - [&](const Se::Symbol *x) { - auto *ctx{builder->getContext()}; - M::Type ty{translateDataRefToFIRType(ctx, dref)}; - gen(x, ty, asAddr); - }, - [&](const auto &x) { gen(x, asAddr); }, - }, - dref.u); - } - - // Note that `NamedEntity` hides its variant member - void gen(const Ev::NamedEntity &ne, bool asAddr) { - if (ne.IsSymbol()) { - // GetFirstSymbol recovers the Symbol* variant - const Se::Symbol *x = &ne.GetFirstSymbol(); - auto *ctx{builder->getContext()}; - M::Type ty{translateSymbolToFIRType(ctx, x)}; - gen(x, ty, asAddr); - } else { - gen(ne.GetComponent(), asAddr); - } - } - - // Common pattern for all optional - template void gen(const std::optional &opt, bool asAddr) { - if (opt.has_value()) { - gen(opt.value(), asAddr); - } - } - - void gen(const Ev::Triplet &triplet, bool asAddr) { - gen(triplet.lower(), asAddr); - gen(triplet.upper(), asAddr); - gen(triplet.stride(), asAddr); - } - - /// Lower an array reference (implies address arithmetic) - /// - /// Visit each of the expressions in each dimension of the array access so as - /// to add them to the data flow. - void gen(const Ev::ArrayRef &aref, bool asAddr) { - // add the base - gen(aref.base(), true); - // add each expression - for (int i = 0, e = aref.size(); i < e; ++i) - std::visit(Co::visitors{ - [&](const Ev::Triplet &t) { gen(t, false); }, - [&](const Ev::IndirectSubscriptIntegerExpr &x) { - gen(x.value(), false); - }, - }, - aref.at(i).u); - visited = ET_ArrayRef; - } - - void gen(const Ev::NullPointer &, bool) { visited = ET_NullPointer; } - - // implies address arithmetic (and inter-image communication) - void gen(const Ev::CoarrayRef &coref, bool asAddr) { - for (auto *sym : coref.base()) { - gen(sym, translateSymbolToFIRType(builder->getContext(), sym), true); - } - for (auto &subs : coref.subscript()) { - std::visit(Co::visitors{ - [&](const Ev::Triplet &x) { gen(x, false); }, - [&](const Ev::IndirectSubscriptIntegerExpr &x) { - gen(x.value(), false); - }, - }, - subs.u); - } - for (auto &cosubs : coref.cosubscript()) { - gen(cosubs, false); - } - visited = ET_CoarrayRef; - } - - // implies address arithmetic - void gen(const Ev::Component &cmpt, bool) { - gen(cmpt.base(), true); - visited = ET_Component; - } - - void gen(const Ev::Substring &subs, bool asAddr) { - gen(subs.lower(), asAddr); - gen(subs.upper(), asAddr); - visited = ET_Substring; - } - - void gen(const Ev::ComplexPart &, bool) { visited = ET_ComplexPart; } - void gen(const Ev::ImpliedDoIndex &, bool) { - // do nothing - } - - void gen(const Ev::StructureConstructor &, bool) { - visited = ET_StructureCtor; - } - - // Skip over constants - void gen(const Ev::BOZLiteralConstant &, bool) { visited = ET_Constant; } - template void gen(const Ev::Constant &, bool) { - visited = ET_Constant; - } - - void gen(const Ev::DescriptorInquiry &, bool) { - TODO(); - visited = ET_DescriptorInquiry; - } - - template - void gen(const Ev::TypeParamInquiry &inquiry, bool asAddr) { - gen(inquiry.base(), asAddr); - visited = ET_TypeParamInquiry; - } - - void gen(const Ev::ProcedureRef &pref, bool asAddr) { - gen(pref.proc(), true); - for (auto &arg : pref.arguments()) { - if (arg.has_value()) { - auto &xarg{arg.value()}; - if (auto *x{xarg.UnwrapExpr()}) { - gen(*x, asAddr); - } else { - auto *sym{xarg.GetAssumedTypeDummy()}; - auto *ctx{builder->getContext()}; - gen(sym, translateSymbolToFIRType(ctx, sym), asAddr); - } - } - } - } - - void gen(const Ev::ProcedureDesignator &des, bool asAddr) { - std::visit(Co::visitors{ - [&](const Ev::SpecificIntrinsic &) { - // do nothing - }, - [&](const Se::Symbol *sym) { - auto *ctx{builder->getContext()}; - gen(sym, translateSymbolToFIRType(ctx, sym), true); - }, - [&](const Co::CopyableIndirection &x) { - gen(x.value(), true); - }, - }, - des.u); - } - - M::FunctionType genFunctionType(const Ev::ProcedureDesignator &proc) { - auto *ctx{builder->getContext()}; - M::Type ty{ - std::visit(Co::visitors{ - [&](const Ev::SpecificIntrinsic &) { - TODO(); /* FIXME */ - return M::Type{}; - }, - [&](const Se::Symbol *x) { - return translateSymbolToFIRType(ctx, x); - }, - [&](const Co::CopyableIndirection &x) { - auto *sym{&x.value().GetLastSymbol()}; - return translateSymbolToFIRType(ctx, sym); - }, - }, - proc.u)}; - return ty.cast(); - } - - /// Lower a function call - /// - /// A call is lowered to a sequence that evaluates the arguments and - /// passes them to a CallOp to the named function. - /// - /// TODO: The function needs to be name-mangled by the front-end or some - /// utility to avoid collisions. - /// - /// %54 = FIR.ApplyExpr(...) - /// %55 = FIR.ApplyExpr(...) - /// %56 = call(@target_func, %54, %55) - template void gen(const Ev::FunctionRef &funref, bool) { - // must be lowered to a call and data flow added for each actual arg - auto *context = builder->getContext(); - M::Location loc = M::UnknownLoc::get(context); // FIXME - - // lookup this function - llvm::StringRef callee = funref.proc().GetName(); - auto module{getModule(builder)}; - auto func = getNamedFunction(callee); - if (!func) { - // create new function - auto funTy{genFunctionType(funref.proc())}; - func = createFunction(module, callee, funTy); - } - assert(func); - - // build arguments - llvm::SmallVector inputs; - for (auto &arg : funref.arguments()) { - if (arg.has_value()) { - if (auto *aa{arg->UnwrapExpr()}) { - auto eops{translateSomeExpr(builder, aa, symbolMap)}; - auto aaType{ - FIRReferenceType::get(translateSomeExprToFIRType(context, aa))}; - M::Value *toLoc{nullptr}; - auto *defOp{getArgs(eops)[0]->getDefiningOp()}; - if (auto load = M::dyn_cast(defOp)) { - toLoc = load.getOperand(); - } else { - auto locate{builder->create( - loc, aa, getDict(eops), getArgs(eops), aaType)}; - toLoc = locate.getResult(); - } - inputs.push_back(toLoc); - } else { - auto *x{arg->GetAssumedTypeDummy()}; - // FIXME: can argument not be passed by reference? - gen(x, translateSymbolToFIRType(context, x), true); - } - } else { - assert(false && "argument is std::nullopt?"); - } - } - - // generate a call - auto call = builder->create(loc, func, inputs); - addResult(call.getResult(0), &funref); - visited = ET_FunctionRef; - } - - template void gen(const Ev::ImpliedDo &ido, bool asAddr) { - gen(ido.lower(), asAddr); - gen(ido.upper(), asAddr); - gen(ido.stride(), asAddr); - } - - template - void gen(const Ev::ArrayConstructor &arrayCtor, bool asAddr) { - for (auto i{arrayCtor.begin()}, end{arrayCtor.end()}; i != end; ++i) { - std::visit([&](auto &x) { gen(x, asAddr); }, i->u); - } - visited = ET_ArrayCtor; - } - - template void gen(const Ev::Relational &op, bool asAddr) { - gen(op.left(), asAddr); - gen(op.right(), asAddr); - visited = ET_Relational; - } - - void gen(const Ev::Relational &op, bool asAddr) { - std::visit([&](const auto &x) { gen(x, asAddr); }, op.u); - } - - template void gen(const Ev::Expr &expr, bool asAddr) { - std::visit([&](const auto &x) { gen(x, asAddr); }, expr.u); - } - - Args getResults() const { return results; } - Dict getDictionary() const { return dictionary; } - ExprType getExprType() const { return visited; } -}; - -// Builds the `([results], [inputs], {dict})` pair for constructing both -// `fir.apply_expr` and `fir.locate_expr` operations. -template -inline Values translateToFIR( - M::OpBuilder *bldr, const SomeExpr *exp, SymMap &map) { - TreeArgsBuilder ab{bldr, map}; - ab.gen(*exp, AddressResult); - return {ab.getResults(), ab.getDictionary(), ab.getExprType()}; -} - -} // namespace - -// `fir.apply_expr` builder -Values Br::translateSomeExpr( - M::OpBuilder *bldr, const SomeExpr *exp, SymMap &map) { - return translateToFIR(bldr, exp, map); -} - -// `fir.locate_expr` builder -Values Br::translateSomeAddrExpr( - M::OpBuilder *bldr, const SomeExpr *exp, SymMap &map) { - return translateToFIR(bldr, exp, map); -} diff --git a/lib/burnside/expression.h b/lib/burnside/expression.h deleted file mode 100644 index 67ce921acd04..000000000000 --- a/lib/burnside/expression.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef FORTRAN_BURNSIDE_EXPRESSION_H_ -#define FORTRAN_BURNSIDE_EXPRESSION_H_ - -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/SmallVector.h" -#include -#include -#include - -/// Conversion of expressions with type Fortran::evaluate::Expr into the FIR -/// dialect of MLIR. -/// -/// Fortran expressions appear in syntactic contexts such as, for example, -/// assignment statements. The semantics of these expressions fall into two -/// cases: (1) an expression that computes a value and (2) an expression that -/// computes an address in storage. In the FIR dialect, case (1) is represented -/// as a "fir.apply_expr" operation. Case (2) is represented as a -/// "fir.locate_expr" operation. - -namespace mlir { -class MLIRContext; -class Value; -class OpBuilder; -} - -namespace Fortran::evaluate { -template class Expr; -struct SomeType; -} - -namespace Fortran::burnside { - -// In the Fortran::burnside namespace, the code will default follow the -// LLVM/MLIR coding standards - -class ApplyExpr; -class LocateExpr; - -enum ExprType { - ET_NONE, // Expr is unclassified type - ET_ArrayCtor, - ET_ArrayRef, - ET_CoarrayRef, - ET_ComplexPart, - ET_Component, - ET_Constant, - ET_DescriptorInquiry, - ET_FunctionRef, - ET_NullPointer, - ET_Operation, - ET_Relational, - ET_StructureCtor, - ET_Substring, - ET_Symbol, - ET_TypeParamInquiry -}; - -class SymMap; -using Args = llvm::SmallVector; -using Dict = std::map; -using Values = std::tuple; -using RewriteVals = mlir::Value *; -using OperandTy = llvm::ArrayRef; -using SomeExpr = evaluate::Expr; - -inline Args getArgs(const Values &values) { return std::get(values); } -inline Dict getDict(const Values &values) { return std::get(values); } -inline ExprType getExprType(const Values &values) { - return std::get(values); -} - -/// Convert an Expr in its implicit dataflow arguments -Values translateSomeExpr( - mlir::OpBuilder *bldr, const SomeExpr *exp, SymMap &map); - -Values translateSomeAddrExpr( - mlir::OpBuilder *bldr, const SomeExpr *exp, SymMap &map); - -} // Fortran::burnside - -#endif // FORTRAN_BURNSIDE_EXPRESSION_H_ diff --git a/lib/burnside/fe-helper.cc b/lib/burnside/fe-helper.cc index 26b42edb5350..df385477f290 100644 --- a/lib/burnside/fe-helper.cc +++ b/lib/burnside/fe-helper.cc @@ -192,12 +192,12 @@ class TypeBuilder { if (lbv.has_value() && ubv.has_value() && isConstant(lbv.value()) && isConstant(ubv.value())) { bounds.emplace_back( - true, toConstant(ubv.value()) - toConstant(lbv.value()) + 1); + toConstant(ubv.value()) - toConstant(lbv.value()) + 1); } else { - bounds.emplace_back(false, 0); + bounds.emplace_back(0); } } else { - bounds.emplace_back(false, 0); + bounds.emplace_back(0); } } return {bounds}; @@ -263,7 +263,7 @@ class TypeBuilder { fir::SequenceType::Shape trivialShape(int size) { fir::SequenceType::Bounds bounds; - bounds.emplace_back(true, size); + bounds.emplace_back(size); return {bounds}; } @@ -300,9 +300,17 @@ M::Location Br::dummyLoc(M::MLIRContext *ctxt) { // What do we need to convert a CharBlock to actual source locations? // FIXME: replace with a map from a provenance to a source location -M::Location Br::parserPosToLoc( - M::MLIRContext &context, const Pa::CharBlock &position) { - return dummyLoc(&context); +M::Location Br::parserPosToLoc(M::MLIRContext &context, + const Pa::CookedSource *cooked, const Pa::CharBlock &position) { + if (cooked) { + auto loc{cooked->GetSourcePositionRange(position)}; + if (loc.has_value()) { + auto &filePos{loc->first}; + return M::FileLineColLoc::get( + filePos.file.path(), filePos.line, filePos.column, &context); + } + } + return M::UnknownLoc::get(&context); } M::Type Br::genTypeFromCategoryAndKind( diff --git a/lib/burnside/fe-helper.h b/lib/burnside/fe-helper.h index 05df769ab3d3..cbbb4e38fc57 100644 --- a/lib/burnside/fe-helper.h +++ b/lib/burnside/fe-helper.h @@ -15,9 +15,10 @@ #ifndef FORTRAN_BURNSIDE_FE_HELPER_H_ #define FORTRAN_BURNSIDE_FE_HELPER_H_ -/// Traversal and coversion of various Fortran::parser data structures into the -/// FIR dialect of MLIR. These traversals are isolated in this file to hopefully -/// make maintenance easier. +/// Traversal and conversion of Fortran type data structures into the FIR +/// dialect of MLIR. +/// +/// [Coding style](https://llvm.org/docs/CodingStandards.html) #include "../common/Fortran.h" #include "mlir/IR/Types.h" @@ -39,6 +40,7 @@ template class Type; namespace Fortran::parser { class CharBlock; +class CookedSource; } namespace Fortran::semantics { @@ -56,14 +58,11 @@ constexpr common::TypeCategory CharacterCat{common::TypeCategory::Character}; constexpr common::TypeCategory LogicalCat{common::TypeCategory::Logical}; constexpr common::TypeCategory DerivedCat{common::TypeCategory::Derived}; -// In the Fortran::burnside namespace, the code will default follow the -// LLVM/MLIR coding standards - mlir::Location dummyLoc(mlir::MLIRContext *ctxt); /// Translate a CharBlock position to (source-file, line, column) -mlir::Location parserPosToLoc( - mlir::MLIRContext &context, const parser::CharBlock &position); +mlir::Location parserPosToLoc(mlir::MLIRContext &context, + const parser::CookedSource *cooked, const parser::CharBlock &position); mlir::Type genTypeFromCategoryAndKind( mlir::MLIRContext *ctxt, common::TypeCategory tc, int kind); diff --git a/lib/burnside/flattened.cc b/lib/burnside/flattened.cc index 57afc150ac45..3d7989dcda9e 100644 --- a/lib/burnside/flattened.cc +++ b/lib/burnside/flattened.cc @@ -24,58 +24,58 @@ namespace flat { // should never approach UINT_MAX. LabelBuilder::LabelBuilder() : referenced(32), counter{0u} {} -LabelRef LabelBuilder::getNext() { - LabelRef next{counter++}; - auto cap{referenced.capacity()}; +LabelMention LabelBuilder::getNext() { + LabelMention next{counter++}; + auto cap{referenced.size()}; if (cap < counter) { - referenced.reserve(2 * cap); + referenced.resize(2 * cap); } - referenced.resize(counter, false); + referenced.reset(next); return next; } -void LabelBuilder::setReferenced(LabelRef label) { - CHECK(label < referenced.size()); - referenced[label] = true; +void LabelBuilder::setReferenced(LabelMention label) { + CHECK(label < referenced.getBitCapacity()); + referenced.set(label); } -bool LabelBuilder::isReferenced(LabelRef label) const { - CHECK(label < referenced.size()); - return referenced[label]; +bool LabelBuilder::isReferenced(LabelMention label) const { + CHECK(label < referenced.getBitCapacity()); + return referenced.test(label); } LabelOp::LabelOp(LabelBuilder &builder) - : builder_{builder}, label_{builder.getNext()} {} + : builder{builder}, label{builder.getNext()} {} LabelOp::LabelOp(const LabelOp &that) - : builder_{that.builder_}, label_{that.label_} {} + : builder{that.builder}, label{that.label} {} LabelOp &LabelOp::operator=(const LabelOp &that) { - CHECK(&builder_ == &that.builder_); - label_ = that.label_; + CHECK(&builder == &that.builder); + label = that.label; return *this; } -void LabelOp::setReferenced() const { builder_.setReferenced(label_); } +void LabelOp::setReferenced() const { builder.setReferenced(label); } -bool LabelOp::isReferenced() const { return builder_.isReferenced(label_); } +bool LabelOp::isReferenced() const { return builder.isReferenced(label); } static void AddAssign(AnalysisData &ad, const semantics::Symbol *symbol, const parser::Label &label) { ad.assignMap[symbol].insert(label); } -std::vector GetAssign( +std::vector GetAssign( AnalysisData &ad, const semantics::Symbol *symbol) { - std::vector result; + std::vector result; for (auto lab : ad.assignMap[symbol]) { result.emplace_back(lab); } return result; } -static std::tuple FindStack( - const std::vector> +static std::tuple FindStack( + const std::vector> &stack, const parser::Name *key) { for (auto iter{stack.rbegin()}, iend{stack.rend()}; iter != iend; ++iter) { @@ -176,7 +176,7 @@ template void errLabelSpec(const A &s, std::list &ops, const parser::Statement &ec, AnalysisData &ad) { if (auto errLab{GetErr(s)}) { - std::optional errRef{FetchLabel(ad, errLab).get()}; + std::optional errRef{FetchLabel(ad, errLab).get()}; LabelOp next{BuildNewLabel(ad)}; ops.emplace_back(SwitchIOOp{s, next, ec.source, errRef}); ops.emplace_back(next); @@ -192,15 +192,15 @@ void threeLabelSpec(const A &s, std::list &ops, auto eorLab{GetEor(s)}; auto endLab{GetEnd(s)}; if (errLab || eorLab || endLab) { - std::optional errRef; + std::optional errRef; if (errLab) { errRef = FetchLabel(ad, errLab).get(); } - std::optional eorRef; + std::optional eorRef; if (eorLab) { eorRef = FetchLabel(ad, eorLab).get(); } - std::optional endRef; + std::optional endRef; if (endLab) { endRef = FetchLabel(ad, endLab).get(); } @@ -213,8 +213,8 @@ void threeLabelSpec(const A &s, std::list &ops, } template -std::vector toLabelRef(AnalysisData &ad, const A &labels) { - std::vector result; +std::vector toLabelMention(AnalysisData &ad, const A &labels) { + std::vector result; for (auto label : labels) { result.emplace_back(FetchLabel(ad, label).get()); } @@ -223,11 +223,11 @@ std::vector toLabelRef(AnalysisData &ad, const A &labels) { } template -std::vector toLabelRef( +std::vector toLabelMention( const LabelOp &next, AnalysisData &ad, const A &labels) { - std::vector result; + std::vector result; result.emplace_back(next); - auto refs{toLabelRef(ad, labels)}; + auto refs{toLabelMention(ad, labels)}; result.insert(result.end(), refs.begin(), refs.end()); CHECK(result.size() == labels.size() + 1); return result; @@ -256,16 +256,17 @@ static std::list getAltReturnLabels(const parser::Call &call) { return result; } -static LabelRef NearestEnclosingDoConstruct(AnalysisData &ad) { - for (auto iterator{ad.nameStack.rbegin()}, endIterator{ad.nameStack.rend()}; +static LabelMention NearestEnclosingDoConstruct(AnalysisData &ad) { + for (auto iterator{ad.constructContextStack.rbegin()}, + endIterator{ad.constructContextStack.rend()}; iterator != endIterator; ++iterator) { auto labelReference{std::get<2>(*iterator)}; - if (labelReference != unspecifiedLabel) { + if (labelReference != UnspecifiedLabel) { return labelReference; } } assert(false && "CYCLE|EXIT not in loop"); - return unspecifiedLabel; + return UnspecifiedLabel; } template std::string GetSource(const A *s) { @@ -276,6 +277,26 @@ template std::string GetSource(const B *s) { return GetSource(&std::get>(s->t)); } +static bool meetsBlockConstraints(const parser::Block &block) { return false; } + +static bool meetsLoopConstraints(const parser::DoConstruct &loop) { + return meetsBlockConstraints(std::get(loop.t)); +} + +static bool meetsWhereConstraints(const parser::IfConstruct &w) { + // we can do better, but for now don't allow ELSE IF constructs + if (std::get>(w.t).empty()) { + const auto &optElse{ + std::get>(w.t)}; + if (optElse.has_value() && + !meetsBlockConstraints(std::get(optElse->t))) { + return false; + } + return meetsBlockConstraints(std::get(w.t)); + } + return false; +} + void Op::Build(std::list &ops, const parser::Statement &ec, AnalysisData &ad) { std::visit( @@ -285,7 +306,7 @@ void Op::Build(std::list &ops, if (hasAltReturns(s.value())) { auto next{BuildNewLabel(ad)}; auto alts{getAltReturnLabels(s.value().v)}; - auto labels{toLabelRef(next, ad, alts)}; + auto labels{toLabelMention(next, ad, alts)}; ops.emplace_back( SwitchOp{s.value(), std::move(labels), ec.source}); ops.emplace_back(next); @@ -300,16 +321,16 @@ void Op::Build(std::list &ops, }, [&](const common::Indirection &s) { ops.emplace_back(GotoOp{s.value(), - s.value().v - ? std::get<2>(FindStack(ad.nameStack, &s.value().v.value())) - : NearestEnclosingDoConstruct(ad), + s.value().v ? std::get<2>(FindStack(ad.constructContextStack, + &s.value().v.value())) + : NearestEnclosingDoConstruct(ad), ec.source}); }, [&](const common::Indirection &s) { ops.emplace_back(GotoOp{s.value(), - s.value().v - ? std::get<1>(FindStack(ad.nameStack, &s.value().v.value())) - : NearestEnclosingDoConstruct(ad), + s.value().v ? std::get<1>(FindStack(ad.constructContextStack, + &s.value().v.value())) + : NearestEnclosingDoConstruct(ad), ec.source}); }, [&](const common::Indirection &s) { @@ -358,14 +379,14 @@ void Op::Build(std::list &ops, }, [&](const common::Indirection &s) { auto next{BuildNewLabel(ad)}; - auto labels{toLabelRef( + auto labels{toLabelMention( next, ad, std::get>(s.value().t))}; ops.emplace_back(SwitchOp{s.value(), std::move(labels), ec.source}); ops.emplace_back(next); }, [&](const common::Indirection &s) { ops.emplace_back(SwitchOp{s.value(), - toLabelRef(ad, + toLabelMention(ad, std::list{std::get<1>(s.value().t), std::get<2>(s.value().t), std::get<3>(s.value().t)}), ec.source}); @@ -373,7 +394,7 @@ void Op::Build(std::list &ops, [&](const common::Indirection &s) { ops.emplace_back( IndirectGotoOp{std::get(s.value().t).symbol, - toLabelRef( + toLabelMention( ad, std::get>(s.value().t))}); }, [&](const common::Indirection &s) { @@ -439,7 +460,8 @@ struct ControlFlowAnalyzer { std::list ops; LabelOp label{buildNewLabel()}; const parser::Name *name{getName(construct)}; - ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); + ad.constructContextStack.emplace_back( + name, GetLabelMention(label), UnspecifiedLabel); appendIfLabeled(std::get<0>(construct.t), ops); ops.emplace_back(BeginOp{construct}); ControlFlowAnalyzer cfa{ops, ad}; @@ -448,7 +470,7 @@ struct ControlFlowAnalyzer { appendIfLabeled(std::get<2>(construct.t), ops); ops.emplace_back(EndOp{construct}); linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); + ad.constructContextStack.pop_back(); return false; } @@ -463,7 +485,8 @@ struct ControlFlowAnalyzer { std::get>(construct.t) .statement.v}; const parser::Name *name{optName ? &*optName : nullptr}; - ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); + ad.constructContextStack.emplace_back( + name, GetLabelMention(label), UnspecifiedLabel); appendIfLabeled( std::get>(construct.t), ops); ops.emplace_back(BeginOp{construct}); @@ -474,60 +497,72 @@ struct ControlFlowAnalyzer { ops.emplace_back(EndOp{construct}); ops.emplace_back(label); linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); + ad.constructContextStack.pop_back(); return false; } + /// `DO` constructs can be lowered to `fir.loop` if they meet some + /// constraints, otherwise they are lowered to a CFG. bool Pre(const parser::DoConstruct &construct) { + if (meetsLoopConstraints(construct)) { + // use `fir.loop` + } std::list ops; LabelOp backedgeLab{buildNewLabel()}; LabelOp incrementLab{buildNewLabel()}; LabelOp entryLab{buildNewLabel()}; LabelOp exitLab{buildNewLabel()}; const parser::Name *name{getName(construct)}; - LabelRef exitOpRef{GetLabelRef(exitLab)}; - ad.nameStack.emplace_back(name, exitOpRef, GetLabelRef(incrementLab)); + LabelMention exitOpRef{GetLabelMention(exitLab)}; + ad.constructContextStack.emplace_back( + name, exitOpRef, GetLabelMention(incrementLab)); appendIfLabeled( std::get>(construct.t), ops); ops.emplace_back(BeginOp{construct}); - ops.emplace_back(GotoOp{GetLabelRef(backedgeLab)}); + ops.emplace_back(GotoOp{GetLabelMention(backedgeLab)}); ops.emplace_back(incrementLab); ops.emplace_back(DoIncrementOp{construct}); ops.emplace_back(backedgeLab); ops.emplace_back(DoCompareOp{construct}); ops.emplace_back(ConditionalGotoOp{ std::get>(construct.t), - GetLabelRef(entryLab), exitOpRef}); + GetLabelMention(entryLab), exitOpRef}); ops.push_back(entryLab); ControlFlowAnalyzer cfa{ops, ad}; Walk(std::get(construct.t), cfa); appendIfLabeled( std::get>(construct.t), ops); - ops.emplace_back(GotoOp{GetLabelRef(incrementLab)}); + ops.emplace_back(GotoOp{GetLabelMention(incrementLab)}); ops.emplace_back(EndOp{construct}); ops.emplace_back(exitLab); linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); + ad.constructContextStack.pop_back(); return false; } + /// `IF` constructs can be lowered to `fir.where` if they meet some + /// constraints, otherwise they are lowered to a CFG. bool Pre(const parser::IfConstruct &construct) { + if (meetsWhereConstraints(construct)) { + // use `fir.where` + } std::list ops; LabelOp thenLab{buildNewLabel()}; LabelOp elseLab{buildNewLabel()}; LabelOp exitLab{buildNewLabel()}; const parser::Name *name{getName(construct)}; - ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel); + ad.constructContextStack.emplace_back( + name, GetLabelMention(exitLab), UnspecifiedLabel); appendIfLabeled( std::get>(construct.t), ops); ops.emplace_back(BeginOp{construct}); ops.emplace_back(ConditionalGotoOp{ std::get>(construct.t), - GetLabelRef(thenLab), GetLabelRef(elseLab)}); + GetLabelMention(thenLab), GetLabelMention(elseLab)}); ops.emplace_back(thenLab); ControlFlowAnalyzer cfa{ops, ad}; Walk(std::get(construct.t), cfa); - LabelRef exitOpRef{GetLabelRef(exitLab)}; + LabelMention exitOpRef{GetLabelMention(exitLab)}; ops.emplace_back(GotoOp{exitOpRef}); for (const auto &elseIfBlock : std::get>(construct.t)) { @@ -538,7 +573,7 @@ struct ControlFlowAnalyzer { LabelOp newElseLab{buildNewLabel()}; ops.emplace_back(ConditionalGotoOp{ std::get>(elseIfBlock.t), - GetLabelRef(newThenLab), GetLabelRef(newElseLab)}); + GetLabelMention(newThenLab), GetLabelMention(newElseLab)}); ops.emplace_back(newThenLab); Walk(std::get(elseIfBlock.t), cfa); ops.emplace_back(GotoOp{exitOpRef}); @@ -558,7 +593,7 @@ struct ControlFlowAnalyzer { std::get>(construct.t), ops); ops.emplace_back(EndOp{construct}); linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); + ad.constructContextStack.pop_back(); return false; } @@ -567,20 +602,21 @@ struct ControlFlowAnalyzer { std::list ops; LabelOp exitLab{buildNewLabel()}; const parser::Name *name{getName(construct)}; - ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel); + ad.constructContextStack.emplace_back( + name, GetLabelMention(exitLab), UnspecifiedLabel); appendIfLabeled(std::get<0>(construct.t), ops); ops.emplace_back(BeginOp{construct}); const auto N{std::get>(construct.t).size()}; - LabelRef exitOpRef{GetLabelRef(exitLab)}; + LabelMention exitOpRef{GetLabelMention(exitLab)}; if (N > 0) { typename std::list::size_type i; std::vector toLabels; for (i = 0; i != N; ++i) { toLabels.emplace_back(buildNewLabel()); } - std::vector targets; + std::vector targets; for (i = 0; i != N; ++i) { - targets.emplace_back(GetLabelRef(toLabels[i])); + targets.emplace_back(GetLabelMention(toLabels[i])); } ops.emplace_back( SwitchOp{construct, targets, std::get<0>(construct.t).source}); @@ -597,7 +633,7 @@ struct ControlFlowAnalyzer { appendIfLabeled(std::get<2>(construct.t), ops); ops.emplace_back(EndOp{construct}); linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); + ad.constructContextStack.pop_back(); return false; } @@ -609,7 +645,8 @@ struct ControlFlowAnalyzer { std::list ops; LabelOp label{buildNewLabel()}; const parser::Name *name{getName(c)}; - ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); + ad.constructContextStack.emplace_back( + name, GetLabelMention(label), UnspecifiedLabel); appendIfLabeled( std::get>(c.t), ops); ops.emplace_back(BeginOp{c}); @@ -623,7 +660,7 @@ struct ControlFlowAnalyzer { std::get>(c.t), ops); ops.emplace_back(EndOp{c}); linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); + ad.constructContextStack.pop_back(); return false; } @@ -631,7 +668,8 @@ struct ControlFlowAnalyzer { std::list ops; LabelOp label{buildNewLabel()}; const parser::Name *name{getName(construct)}; - ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel); + ad.constructContextStack.emplace_back( + name, GetLabelMention(label), UnspecifiedLabel); appendIfLabeled( std::get>(construct.t), ops); @@ -643,7 +681,7 @@ struct ControlFlowAnalyzer { std::get>(construct.t), ops); ops.emplace_back(EndOp{construct}); linearOps.splice(linearOps.end(), ops); - ad.nameStack.pop_back(); + ad.constructContextStack.pop_back(); return false; } @@ -652,12 +690,12 @@ struct ControlFlowAnalyzer { return optName ? &*optName : nullptr; } - LabelRef GetLabelRef(const LabelOp &label) { + LabelMention GetLabelMention(const LabelOp &label) { label.setReferenced(); return label; } - LabelRef GetLabelRef(const parser::Label &label) { + LabelMention GetLabelMention(const parser::Label &label) { return FetchLabel(ad, label); } diff --git a/lib/burnside/flattened.h b/lib/burnside/flattened.h index b22607f4cf7b..f5c795c04eaa 100644 --- a/lib/burnside/flattened.h +++ b/lib/burnside/flattened.h @@ -15,9 +15,9 @@ #ifndef FORTRAN_BURNSIDE_FLATTENED_H_ #define FORTRAN_BURNSIDE_FLATTENED_H_ -#include "common.h" #include "mixin.h" #include "../parser/parse-tree.h" +#include "llvm/ADT/BitVector.h" #include #include #include @@ -30,12 +30,14 @@ struct AnalysisData; namespace flat { -// This is a flattened, linearized representation of the parse tree. It captures -// the executable specification of the input program. The flattened IR can be -// used to construct the Fortran IR. +/// This is a flattened, linearized representation of the parse +/// tree. It captures the executable specification of the input +/// program. The flattened IR can be used to construct FIR. +/// +/// [Coding style](https://llvm.org/docs/CodingStandards.html) -using LabelRef = unsigned; -constexpr LabelRef unspecifiedLabel{UINT_MAX}; +using LabelMention = unsigned; +constexpr LabelMention UnspecifiedLabel{UINT_MAX}; using Location = parser::CharBlock; struct LabelBuilder; @@ -47,12 +49,12 @@ struct LabelOp { LabelOp &operator=(const LabelOp &that); void setReferenced() const; bool isReferenced() const; - LabelRef get() const { return label_; } - operator LabelRef() const { return get(); } + LabelMention get() const { return label; } + operator LabelMention() const { return get(); } private: - LabelBuilder &builder_; - LabelRef label_; + LabelBuilder &builder; + LabelMention label; }; struct ArtificialJump {}; @@ -62,12 +64,12 @@ struct GotoOp : public SumTypeCopyMixin { template - explicit GotoOp(const A &stmt, LabelRef dest, const Location &source) + explicit GotoOp(const A &stmt, LabelMention dest, const Location &source) : SumTypeCopyMixin{&stmt}, target{dest}, source{source} {} - explicit GotoOp(LabelRef dest) + explicit GotoOp(LabelMention dest) : SumTypeCopyMixin{ArtificialJump{}}, target{dest} {} - LabelRef target; + LabelMention target; Location source; }; @@ -87,21 +89,21 @@ struct ConditionalGotoOp const parser::Statement *, const parser::IfStmt *, const parser::Statement *> { template - explicit ConditionalGotoOp(const A &cond, LabelRef tb, LabelRef fb) + explicit ConditionalGotoOp(const A &cond, LabelMention tb, LabelMention fb) : SumTypeCopyMixin{&cond}, trueLabel{tb}, falseLabel{fb} {} - LabelRef trueLabel; - LabelRef falseLabel; + LabelMention trueLabel; + LabelMention falseLabel; }; // multi-way branch based on a target-value of a variable struct IndirectGotoOp { explicit IndirectGotoOp( - const semantics::Symbol *symbol, std::vector &&labelRefs) - : symbol{symbol}, labelRefs{labelRefs} {} + const semantics::Symbol *symbol, std::vector &&labelRefs) + : labelRefs{labelRefs}, symbol{symbol} {} + std::vector labelRefs; const semantics::Symbol *symbol; - std::vector labelRefs; }; // intrinsic IO operations can return with an implied multi-way branch @@ -112,18 +114,17 @@ struct SwitchIOOp const parser::EndfileStmt *, const parser::RewindStmt *, const parser::FlushStmt *, const parser::InquireStmt *> { template - explicit SwitchIOOp(const A &io, LabelRef next, const Location &source, - std::optional errLab, - std::optional eorLab = std::nullopt, - std::optional endLab = std::nullopt) + explicit SwitchIOOp(const A &io, LabelMention next, const Location &source, + std::optional errLab, + std::optional eorLab = std::nullopt, + std::optional endLab = std::nullopt) : SumTypeCopyMixin{&io}, next{next}, source{source}, errLabel{errLab}, eorLabel{eorLab}, endLabel{endLab} {} - - LabelRef next; + LabelMention next; Location source; - std::optional errLabel; - std::optional eorLabel; - std::optional endLabel; + std::optional errLabel; + std::optional eorLabel; + std::optional endLabel; }; // multi-way branch based on conditions @@ -133,11 +134,11 @@ struct SwitchOp const parser::CaseConstruct *, const parser::SelectRankConstruct *, const parser::SelectTypeConstruct *> { template - explicit SwitchOp( - const A &sw, const std::vector &refs, const Location &source) + explicit SwitchOp(const A &sw, const std::vector &refs, + const Location &source) : SumTypeCopyMixin{&sw}, refs{refs}, source{source} {} - const std::vector refs; + const std::vector refs; Location source; }; @@ -197,24 +198,26 @@ struct Op : public SumTypeMixin referenced; + LabelMention getNext(); + void setReferenced(LabelMention label); + bool isReferenced(LabelMention label) const; + llvm::BitVector referenced; unsigned counter; }; LabelOp FetchLabel(AnalysisData &ad, const parser::Label &label); -std::vector GetAssign( +std::vector GetAssign( AnalysisData &ad, const semantics::Symbol *symbol); } // namespace flat +// Collection of data maintained internally by the flattening algorithm struct AnalysisData { std::map labelMap; - std::vector> - nameStack; + std::vector< + std::tuple> + constructContextStack; flat::LabelBuilder labelBuilder; std::map> assignMap; }; diff --git a/lib/burnside/mixin.h b/lib/burnside/mixin.h index 853b7734fcc8..f24ccc2b1b08 100644 --- a/lib/burnside/mixin.h +++ b/lib/burnside/mixin.h @@ -20,6 +20,8 @@ // better to think of these as "included in" a class, rather than as an // "inherited from" base class. +/// [Coding style](https://llvm.org/docs/CodingStandards.html) + #include "llvm/ADT/ilist.h" #include #include diff --git a/lib/burnside/runtime.h b/lib/burnside/runtime.h index 5275f33b586c..48096775bc25 100644 --- a/lib/burnside/runtime.h +++ b/lib/burnside/runtime.h @@ -27,8 +27,7 @@ class MLIRContext; namespace Fortran::burnside { -// In the Fortran::burnside namespace, the code will default follow the -// LLVM/MLIR coding standards +/// [Coding style](https://llvm.org/docs/CodingStandards.html) #define DEFINE_RUNTIME_ENTRY(A, B, C, D) FIRT_##A, enum RuntimeEntryCode { diff --git a/lib/fir/.clang-format b/lib/fir/.clang-format new file mode 100644 index 000000000000..392e20189554 --- /dev/null +++ b/lib/fir/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes \ No newline at end of file diff --git a/lib/fir/Attribute.cpp b/lib/fir/Attribute.cpp index 46a2eeac54c5..7c7bdd05f46c 100644 --- a/lib/fir/Attribute.cpp +++ b/lib/fir/Attribute.cpp @@ -15,12 +15,12 @@ #include "fir/Attribute.h" #include "fir/Dialect.h" #include "fir/Type.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" #include "mlir/IR/AttributeSupport.h" #include "mlir/IR/Diagnostics.h" #include "mlir/IR/Types.h" #include "mlir/Parser.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" namespace L = llvm; namespace M = mlir; @@ -34,7 +34,7 @@ namespace { class AttributeParser { public: AttributeParser(L::StringRef rawText) - : srcBuff{rawText}, srcPtr{rawText.begin()} {} + : srcBuff{rawText}, srcPtr{rawText.begin()} {} M::Attribute parseAttribute(FIROpsDialect *dialect, M::Location loc) { skipWhitespace(); @@ -87,8 +87,11 @@ class AttributeParser { case '\n': case '\r': case '\t': - case '\v': ++srcPtr; continue; - default: break; + case '\v': + ++srcPtr; + continue; + default: + break; } break; } @@ -117,7 +120,7 @@ class AttributeParser { const char *srcPtr; }; -} // namespace +} // namespace namespace fir { namespace detail { @@ -132,15 +135,15 @@ struct TypeAttributeStorage : public M::AttributeStorage { bool operator==(const KeyTy &key) const { return key == value; } /// Construct a new storage instance. - static TypeAttributeStorage *construct( - M::AttributeStorageAllocator &allocator, KeyTy key) { + static TypeAttributeStorage * + construct(M::AttributeStorageAllocator &allocator, KeyTy key) { return new (allocator.allocate()) TypeAttributeStorage(key); } M::Type value; }; -} // detail +} // namespace detail ExactTypeAttr ExactTypeAttr::get(M::Type value) { return Base::get(value.getContext(), FIR_EXACTTYPE, value); @@ -170,9 +173,9 @@ PointIntervalAttr PointIntervalAttr::get(mlir::MLIRContext *ctxt) { } M::Attribute parseFirAttribute(FIROpsDialect *dialect, L::StringRef rawText, - M::Type type, M::Location loc) { + M::Type type, M::Location loc) { AttributeParser parser{rawText}; return parser.parseAttribute(dialect, loc); } -} // fir +} // namespace fir diff --git a/lib/fir/CMakeLists.txt b/lib/fir/CMakeLists.txt index 253b2f8bd7dc..dbe3a5e265fc 100644 --- a/lib/fir/CMakeLists.txt +++ b/lib/fir/CMakeLists.txt @@ -16,6 +16,7 @@ add_library(FIR Attribute.cpp Dialect.cpp FIROps.cpp + IteratedDominanceFrontier.cpp LLVMConverter.cpp MemToReg.cpp StdConverter.cpp diff --git a/lib/fir/Dialect.cpp b/lib/fir/Dialect.cpp index 0f29ce87b1f1..67a9987af341 100644 --- a/lib/fir/Dialect.cpp +++ b/lib/fir/Dialect.cpp @@ -13,10 +13,10 @@ // limitations under the License. #include "fir/Dialect.h" +#include "../evaluate/expression.h" #include "fir/Attribute.h" #include "fir/FIROps.h" #include "fir/Type.h" -#include "../evaluate/expression.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/IR/StandardTypes.h" @@ -26,9 +26,10 @@ using namespace fir; namespace { -template +template void selectBuild(M::OpBuilder *builder, M::OperationState *result, - M::Value *condition, llvm::ArrayRef tuples) { + M::Value *condition, + llvm::ArrayRef tuples) { result->addOperands(condition); for (auto &tup : tuples) { auto *cond{std::get(tup)}; @@ -42,34 +43,34 @@ void selectBuild(M::OpBuilder *builder, M::OperationState *result, result->addSuccessor(block, blkArgs); } } -} // namespace +} // namespace fir::FIROpsDialect::FIROpsDialect(M::MLIRContext *ctx) - : M::Dialect("fir", ctx) { + : M::Dialect("fir", ctx) { addTypes(); + FieldType, HeapType, IntType, LogicalType, PointerType, RealType, + RecordType, ReferenceType, SequenceType, TypeDescType>(); addAttributes(); + PointIntervalAttr, SubclassAttr, UpperBoundAttr>(); addOperations(); + >(); } // anchor the class vtable fir::FIROpsDialect::~FIROpsDialect() {} -M::Type fir::FIROpsDialect::parseType( - llvm::StringRef rawData, M::Location loc) const { +M::Type fir::FIROpsDialect::parseType(llvm::StringRef rawData, + M::Location loc) const { return parseFirType(const_cast(this), rawData, loc); } void printBounds(llvm::raw_ostream &os, const SequenceType::Bounds &bounds) { char ch = '<'; for (auto &b : bounds) { - if (b.known) { - os << ch << b.bound; + if (b.hasValue()) { + os << ch << *b; } else { os << ch << '?'; } @@ -110,8 +111,8 @@ void fir::FIROpsDialect::printType(M::Type ty, llvm::raw_ostream &os) const { } else if (auto type = ty.dyn_cast()) { os << "array"; auto shape = type.getShape(); - if (shape.known) { - printBounds(os, shape.bounds); + if (shape.hasValue()) { + printBounds(os, *shape); } else { os << "<*"; } @@ -156,14 +157,15 @@ void fir::FIROpsDialect::printType(M::Type ty, llvm::raw_ostream &os) const { } } -M::Attribute fir::FIROpsDialect::parseAttribute( - llvm::StringRef rawText, M::Type type, M::Location loc) const { - return parseFirAttribute( - const_cast(this), rawText, type, loc); +M::Attribute fir::FIROpsDialect::parseAttribute(llvm::StringRef rawText, + M::Type type, + M::Location loc) const { + return parseFirAttribute(const_cast(this), rawText, type, + loc); } -void fir::FIROpsDialect::printAttribute( - M::Attribute attr, llvm::raw_ostream &os) const { +void fir::FIROpsDialect::printAttribute(M::Attribute attr, + llvm::raw_ostream &os) const { if (auto exact = attr.dyn_cast()) { os << fir::ExactTypeAttr::getAttrName() << '<' << exact.getType() << '>'; } else if (auto sub = attr.dyn_cast()) { diff --git a/lib/fir/FIROps.cpp b/lib/fir/FIROps.cpp index a923fbb31f32..ab3df559de0a 100644 --- a/lib/fir/FIROps.cpp +++ b/lib/fir/FIROps.cpp @@ -27,51 +27,62 @@ namespace fir { // AllocaOp M::Type AllocaOp::getAllocatedType() { + // return getAttrOfType("in_type").getType(); return getType().cast().getEleTy(); } +M::Type AllocaOp::getRefTy(M::Type ty) { return ReferenceType::get(ty); } + +// AllocMemOp + +M::Type AllocMemOp::getAllocatedType() { + // return getAttrOfType("in_type").getType(); + return getType().cast().getEleTy(); +} + +M::Type AllocMemOp::getRefTy(M::Type ty) { return HeapType::get(ty); } + // DispatchTableOp void DispatchTableOp::build(M::Builder *builder, M::OperationState *result, - L::StringRef name, M::Type type, L::ArrayRef attrs) { - result->addAttribute( - M::SymbolTable::getSymbolAttrName(), builder->getStringAttr(name)); + L::StringRef name, M::Type type, + L::ArrayRef attrs) { + result->addAttribute("method", builder->getStringAttr(name)); for (const auto &pair : attrs) { result->addAttribute(pair.first, pair.second); } } -M::ParseResult DispatchTableOp::parse( - M::OpAsmParser *parser, M::OperationState *result) { +M::ParseResult DispatchTableOp::parse(M::OpAsmParser &parser, + M::OperationState &result) { // Parse the name as a symbol reference attribute. SymbolRefAttr nameAttr; - if (parser->parseAttribute( - nameAttr, M::SymbolTable::getSymbolAttrName(), result->attributes)) + if (parser.parseAttribute(nameAttr, "method", result.attributes)) return failure(); // Convert the parsed name attr into a string attr. - result->attributes.back().second = - parser->getBuilder().getStringAttr(nameAttr.getValue()); + result.attributes.back().second = + parser.getBuilder().getStringAttr(nameAttr.getValue()); // Parse the optional table body. - M::Region *body = result->addRegion(); - if (parser->parseOptionalRegion(*body, - L::ArrayRef{}, L::ArrayRef{})) + M::Region *body = result.addRegion(); + if (parser.parseOptionalRegion(*body, + L::ArrayRef{}, + L::ArrayRef{})) return M::failure(); - ensureTerminator(*body, parser->getBuilder(), result->location); + ensureTerminator(*body, parser.getBuilder(), result.location); return M::success(); } -void DispatchTableOp::print(M::OpAsmPrinter *p) { - auto tableName = - getAttrOfType(M::SymbolTable::getSymbolAttrName()).getValue(); - *p << getOperationName() << " @" << tableName; +void DispatchTableOp::print(M::OpAsmPrinter &p) { + auto tableName = getAttrOfType("method").getValue(); + p << getOperationName() << " @" << tableName; Region &body = getOperation()->getRegion(0); if (!body.empty()) - p->printRegion(body, /*printEntryBlockArgs=*/false, - /*printBlockTerminators=*/false); + p.printRegion(body, /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/false); } M::LogicalResult DispatchTableOp::verify() { return M::success(); } @@ -85,102 +96,66 @@ void DispatchTableOp::appendTableEntry(M::Operation *op) { front().front().push_back(op); } -M::ParseResult parseCallOp(M::OpAsmParser *parser, M::OperationState *result) { - M::FunctionType calleeType; - L::SmallVector operands; - M::OpAsmParser::OperandType callee; - auto calleeLoc = parser->getNameLoc(); - if (parser->parseOperand(callee)) { - M::SymbolRefAttr calleeAttr; - if (parser->parseAttribute(calleeAttr, "proc", result->attributes) || - parser->parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || - parser->parseOptionalAttributeDict(result->attributes) || - parser->parseColonType(calleeType) || - parser->addTypesToList(calleeType.getResults(), result->types) || - parser->resolveOperands( - operands, calleeType.getInputs(), calleeLoc, result->operands)) - return M::failure(); - } else { - result->attributes.push_back(parser->getBuilder().getNamedAttr( - "proc", parser->getBuilder().getSymbolRefAttr(""))); - if (parser->getCurrentLocation(&calleeLoc) || - parser->parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || - parser->parseOptionalAttributeDict(result->attributes) || - parser->parseColonType(calleeType) || - parser->resolveOperand(callee, calleeType, result->operands) || - parser->resolveOperands( - operands, calleeType.getInputs(), calleeLoc, result->operands) || - parser->addTypesToList(calleeType.getResults(), result->types)) - return M::failure(); - } - return M::success(); -} - -mlir::ParseResult parseDispatchOp( - mlir::OpAsmParser *parser, mlir::OperationState *result) { - M::FunctionType calleeType; - L::SmallVector operands; - auto calleeLoc = parser->getNameLoc(); - M::StringAttr calleeAttr; - if (parser->parseAttribute(calleeAttr, "proc", result->attributes) || - parser->parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || - parser->parseOptionalAttributeDict(result->attributes) || - parser->parseColonType(calleeType) || - parser->addTypesToList(calleeType.getResults(), result->types) || - parser->resolveOperands( - operands, calleeType.getInputs(), calleeLoc, result->operands)) - return M::failure(); - return M::success(); -} - // GlobalOp void GlobalOp::build(M::Builder *builder, M::OperationState *result, - L::StringRef name, M::Type type, L::ArrayRef attrs) { + L::StringRef name, M::Type type, + L::ArrayRef attrs) { result->addAttribute(getTypeAttrName(), builder->getTypeAttr(type)); - result->addAttribute( - M::SymbolTable::getSymbolAttrName(), builder->getStringAttr(name)); + result->addAttribute(M::SymbolTable::getSymbolAttrName(), + builder->getStringAttr(name)); for (const auto &pair : attrs) { result->addAttribute(pair.first, pair.second); } } -M::ParseResult GlobalOp::parse( - M::OpAsmParser *parser, M::OperationState *result) { +M::ParseResult GlobalOp::parse(M::OpAsmParser &parser, + M::OperationState &result) { // Parse the name as a symbol reference attribute. SymbolRefAttr nameAttr; - if (parser->parseAttribute( - nameAttr, M::SymbolTable::getSymbolAttrName(), result->attributes)) + if (parser.parseAttribute(nameAttr, M::SymbolTable::getSymbolAttrName(), + result.attributes)) return failure(); - auto &builder = parser->getBuilder(); - result->attributes.back().second = builder.getStringAttr(nameAttr.getValue()); + auto &builder = parser.getBuilder(); + result.attributes.back().second = builder.getStringAttr(nameAttr.getValue()); + + if (parser.parseOptionalKeyword("constant")) { + result.addAttribute("constant", builder.getBoolAttr(false)); + } else { + // if "constant" keyword then mark this as a constant, not a variable + result.addAttribute("constant", builder.getBoolAttr(true)); + } M::Type globalType; - if (parser->parseColonType(globalType)) { + if (parser.parseColonType(globalType)) { return M::failure(); } - result->addAttribute(getTypeAttrName(), builder.getTypeAttr(globalType)); + result.addAttribute(getTypeAttrName(), builder.getTypeAttr(globalType)); // Parse the optional initializer body. - M::Region *body = result->addRegion(); - if (parser->parseOptionalRegion(*body, - L::ArrayRef{}, L::ArrayRef{})) + M::Region *body = result.addRegion(); + if (parser.parseOptionalRegion(*body, + L::ArrayRef{}, + L::ArrayRef{})) return M::failure(); - ensureTerminator(*body, builder, result->location); + ensureTerminator(*body, builder, result.location); return M::success(); } -void GlobalOp::print(M::OpAsmPrinter *p) { +void GlobalOp::print(M::OpAsmPrinter &p) { auto varName = getAttrOfType(M::SymbolTable::getSymbolAttrName()).getValue(); - *p << getOperationName() << " @" << varName << " : "; - p->printType(getType()); + p << getOperationName() << " @" << varName; + if (getAttr("constant").cast().getValue()) + p << " constant"; + p << " : "; + p.printType(getType()); Region &body = getOperation()->getRegion(0); if (!body.empty()) - p->printRegion(body, /*printEntryBlockArgs=*/false, - /*printBlockTerminators=*/false); + p.printRegion(body, /*printEntryBlockArgs=*/false, + /*printBlockTerminators=*/false); } M::LogicalResult GlobalOp::verify() { return M::success(); } @@ -217,54 +192,54 @@ M::ParseResult LoadOp::getElementOf(M::Type &ele, M::Type ref) { // LoopOp -void LoopOp::build(M::Builder *builder, M::OperationState *result, M::Value *lb, - M::Value *ub, L::ArrayRef step) { +void LoopOp::build(M::Builder *builder, M::OperationState &result, M::Value *lb, + M::Value *ub, L::ArrayRef step) { if (step.empty()) - result->addOperands({lb, ub}); + result.addOperands({lb, ub}); else - result->addOperands({lb, ub, step[0]}); - M::Region *bodyRegion = result->addRegion(); - LoopOp::ensureTerminator(*bodyRegion, *builder, result->location); + result.addOperands({lb, ub, step[0]}); + M::Region *bodyRegion = result.addRegion(); + LoopOp::ensureTerminator(*bodyRegion, *builder, result.location); bodyRegion->front().addArgument(builder->getIndexType()); } -M::ParseResult parseLoopOp(M::OpAsmParser *parser, M::OperationState *result) { - auto &builder = parser->getBuilder(); +M::ParseResult parseLoopOp(M::OpAsmParser &parser, M::OperationState &result) { + auto &builder = parser.getBuilder(); M::OpAsmParser::OperandType inductionVariable, lb, ub, step; // Parse the induction variable followed by '='. - if (parser->parseRegionArgument(inductionVariable) || parser->parseEqual()) + if (parser.parseRegionArgument(inductionVariable) || parser.parseEqual()) return M::failure(); // Parse loop bounds. M::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)) + if (parser.parseOperand(lb) || + parser.resolveOperand(lb, indexType, result.operands) || + parser.parseKeyword("to") || parser.parseOperand(ub) || + parser.resolveOperand(ub, indexType, result.operands)) return M::failure(); - if (parser->parseOptionalKeyword(LoopOp::getStepKeyword())) { - result->addAttribute(LoopOp::getStepKeyword(), - builder.getIntegerAttr(builder.getIndexType(), 1)); - } else if (parser->parseOperand(step) || - parser->resolveOperand(step, indexType, result->operands)) { + if (parser.parseOptionalKeyword(LoopOp::getStepKeyword())) { + result.addAttribute(LoopOp::getStepKeyword(), + builder.getIntegerAttr(builder.getIndexType(), 1)); + } else if (parser.parseOperand(step) || + parser.resolveOperand(step, indexType, result.operands)) { return M::failure(); } - if (parser->parseOptionalKeyword("unordered")) { + if (parser.parseOptionalKeyword("unordered")) { // ok } else { - result->addAttribute("unordered", builder.getBoolAttr(true)); + result.addAttribute("unordered", builder.getBoolAttr(true)); } // Parse the body region. - M::Region *body = result->addRegion(); - if (parser->parseRegion(*body, inductionVariable, indexType)) + M::Region *body = result.addRegion(); + if (parser.parseRegion(*body, inductionVariable, indexType)) return M::failure(); - fir::LoopOp::ensureTerminator(*body, builder, result->location); + fir::LoopOp::ensureTerminator(*body, builder, result.location); // Parse the optional attribute list. - if (parser->parseOptionalAttributeDict(result->attributes)) { + if (parser.parseOptionalAttributeDict(result.attributes)) { return M::failure(); } return M::success(); @@ -283,53 +258,55 @@ fir::LoopOp getForInductionVarOwner(M::Value *val) { // StoreOp M::Type StoreOp::elementType(M::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(); + 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 {}; } // WhereOp -void WhereOp::build(M::Builder *builder, M::OperationState *result, - M::Value *cond, bool withElseRegion) { - result->addOperands(cond); - M::Region *thenRegion = result->addRegion(); - M::Region *elseRegion = result->addRegion(); - WhereOp::ensureTerminator(*thenRegion, *builder, result->location); +void WhereOp::build(M::Builder *builder, M::OperationState &result, + M::Value *cond, bool withElseRegion) { + result.addOperands(cond); + M::Region *thenRegion = result.addRegion(); + M::Region *elseRegion = result.addRegion(); + WhereOp::ensureTerminator(*thenRegion, *builder, result.location); if (withElseRegion) - WhereOp::ensureTerminator(*elseRegion, *builder, result->location); + WhereOp::ensureTerminator(*elseRegion, *builder, result.location); } -M::ParseResult parseWhereOp(M::OpAsmParser *parser, M::OperationState *result) { +M::ParseResult parseWhereOp(M::OpAsmParser &parser, M::OperationState &result) { // Create the regions for 'then'. - result->regions.reserve(2); - M::Region *thenRegion = result->addRegion(); - M::Region *elseRegion = result->addRegion(); + result.regions.reserve(2); + M::Region *thenRegion = result.addRegion(); + M::Region *elseRegion = result.addRegion(); - auto &builder = parser->getBuilder(); + auto &builder = parser.getBuilder(); M::OpAsmParser::OperandType cond; M::Type i1Type = builder.getIntegerType(1); - if (parser->parseOperand(cond) || - parser->resolveOperand(cond, i1Type, result->operands)) + if (parser.parseOperand(cond) || + parser.resolveOperand(cond, i1Type, result.operands)) return M::failure(); - if (parser->parseRegion(*thenRegion, {}, {})) { + if (parser.parseRegion(*thenRegion, {}, {})) { return M::failure(); } - WhereOp::ensureTerminator( - *thenRegion, parser->getBuilder(), result->location); + WhereOp::ensureTerminator(*thenRegion, parser.getBuilder(), result.location); - if (!parser->parseOptionalKeyword("otherwise")) { - if (parser->parseRegion(*elseRegion, {}, {})) { + if (!parser.parseOptionalKeyword("otherwise")) { + if (parser.parseRegion(*elseRegion, {}, {})) { return M::failure(); } - WhereOp::ensureTerminator( - *elseRegion, parser->getBuilder(), result->location); + WhereOp::ensureTerminator(*elseRegion, parser.getBuilder(), + result.location); } // Parse the optional attribute list. - if (parser->parseOptionalAttributeDict(result->attributes)) { + if (parser.parseOptionalAttributeDict(result.attributes)) { return M::failure(); } @@ -360,14 +337,14 @@ unsigned getCaseArgumentOffset(L::ArrayRef cases, unsigned dest) { 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(); +M::ParseResult parseSelector(M::OpAsmParser &parser, M::OperationState &result, + M::OpAsmParser::OperandType &selector, + M::Type &type) { + if (parser.parseOperand(selector) || parser.parseColonType(type) || + parser.resolveOperand(selector, type, result.operands) || + parser.parseLSquare()) + return M::failure(); + return M::success(); } // Tablegen operators @@ -375,4 +352,4 @@ mlir::ParseResult parseSelector(mlir::OpAsmParser *parser, #define GET_OP_CLASSES #include "fir/FIROps.cpp.inc" -} // fir +} // namespace fir diff --git a/lib/fir/IteratedDominanceFrontier.cpp b/lib/fir/IteratedDominanceFrontier.cpp new file mode 100644 index 000000000000..d35ce321bc7a --- /dev/null +++ b/lib/fir/IteratedDominanceFrontier.cpp @@ -0,0 +1,107 @@ +//===- IteratedDominanceFrontier.cpp - Compute IDF ------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Compute iterated dominance frontiers using a linear time algorithm. +// +//===----------------------------------------------------------------------===// + +#include "fir/Analysis/IteratedDominanceFrontier.h" +#include "mlir/Analysis/Dominance.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Dominators.h" +#include +#include + +namespace fir { + +template +void IDFCalculator::calculate( + llvm::SmallVectorImpl &PHIBlocks) { + // Use a priority queue keyed on dominator tree level so that inserted nodes + // are handled from the bottom of the dominator tree upwards. We also augment + // the level with a DFS number to ensure that the blocks are ordered in a + // deterministic way. + using UnsignedPair = std::pair; + using DomTreeNode = llvm::DomTreeNodeBase; + using DomTreeNodePair = std::pair; + using IDFPriorityQueue = + std::priority_queue, + llvm::less_second>; + IDFPriorityQueue PQ; + + if (DefBlocks->empty()) + return; + + DT.updateDFSNumbers(); + + for (NodeTy *BB : *DefBlocks) { + if (DomTreeNode *Node = DT.getNode(BB)) + PQ.push({Node, std::make_pair(Node->getLevel(), Node->getDFSNumIn())}); + } + + llvm::SmallVector Worklist; + llvm::SmallPtrSet VisitedPQ; + llvm::SmallPtrSet VisitedWorklist; + + while (!PQ.empty()) { + DomTreeNodePair RootPair = PQ.top(); + PQ.pop(); + DomTreeNode *Root = RootPair.first; + unsigned RootLevel = RootPair.second.first; + + // Walk all dominator tree children of Root, inspecting their CFG edges with + // targets elsewhere on the dominator tree. Only targets whose level is at + // most Root's level are added to the iterated dominance frontier of the + // definition set. + + Worklist.clear(); + Worklist.push_back(Root); + VisitedWorklist.insert(Root); + + while (!Worklist.empty()) { + DomTreeNode *Node = Worklist.pop_back_val(); + NodeTy *BB = Node->getBlock(); + // Succ is the successor in the direction we are calculating IDF, so it is + // successor for IDF, and predecessor for Reverse IDF. + auto DoWork = [&](NodeTy *Succ) { + DomTreeNode *SuccNode = DT.getNode(Succ); + + const unsigned SuccLevel = SuccNode->getLevel(); + if (SuccLevel > RootLevel) + return; + + if (!VisitedPQ.insert(SuccNode).second) + return; + + NodeTy *SuccBB = SuccNode->getBlock(); + if (useLiveIn && !LiveInBlocks->count(SuccBB)) + return; + + PHIBlocks.emplace_back(SuccBB); + if (!DefBlocks->count(SuccBB)) + PQ.push(std::make_pair( + SuccNode, std::make_pair(SuccLevel, SuccNode->getDFSNumIn()))); + }; + + for (auto *Succ : BB->getSuccessors()) + DoWork(Succ); + + for (auto DomChild : *Node) { + if (VisitedWorklist.insert(DomChild).second) + Worklist.push_back(DomChild); + } + } + } +} + +template class IDFCalculator; + +} // namespace fir diff --git a/lib/fir/LLVMConverter.cpp b/lib/fir/LLVMConverter.cpp index c89e37414372..f57a19bab3f4 100644 --- a/lib/fir/LLVMConverter.cpp +++ b/lib/fir/LLVMConverter.cpp @@ -16,13 +16,6 @@ #include "fir/Dialect.h" #include "fir/FIROps.h" #include "fir/Type.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/Config/abi-breaking.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Type.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/raw_ostream.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h" #include "mlir/Dialect/AffineOps/AffineOps.h" @@ -32,6 +25,13 @@ #include "mlir/Pass/Pass.h" #include "mlir/Target/LLVMIR.h" #include "mlir/Transforms/DialectConversion.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Config/abi-breaking.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" // This module performs the conversion of FIR operations to MLIR standard and/or // LLVM-IR dialects. @@ -54,17 +54,23 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { using LLVMTypeConverter::LLVMTypeConverter; // convert a front-end kind value to either a std or LLVM IR dialect type - static M::Type kindToRealType( - M::MLIRContext *ctx, L::LLVMContext &llvmCtx, KindTy kind) { + static M::Type kindToRealType(M::MLIRContext *ctx, L::LLVMContext &llvmCtx, + KindTy kind) { switch (kind) { - case 0: return M::FloatType::getF32(ctx); // FIXME: use defaulted kind - case 2: return M::FloatType::getF16(ctx); - case 3: return M::FloatType::getBF16(ctx); - case 4: return M::FloatType::getF32(ctx); - case 8: return M::FloatType::getF64(ctx); + case 0: + return M::FloatType::getF32(ctx); // FIXME: use defaulted kind + case 2: + return M::FloatType::getF16(ctx); + case 3: + return M::FloatType::getBF16(ctx); + case 4: + return M::FloatType::getF32(ctx); + case 8: + return M::FloatType::getF64(ctx); case 10: return M::LLVM::LLVMType::get(ctx, L::Type::getX86_FP80Ty(llvmCtx)); - case 16: return M::LLVM::LLVMType::get(ctx, L::Type::getFP128Ty(llvmCtx)); + case 16: + return M::LLVM::LLVMType::get(ctx, L::Type::getFP128Ty(llvmCtx)); } assert(!kind && "unhandled kind"); return {}; @@ -77,7 +83,8 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { return M::LLVM::LLVMType::get(ctx, i64->getPointerTo()); } - template M::Type convertPointerLike(A &ty) { + template + M::Type convertPointerLike(A &ty) { M::Type ele = ty.getEleTy(); auto eleTy = convertType(ele); if (ele.dyn_cast()) { @@ -120,16 +127,16 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { // (buffer*, buffer-size) L::SmallVector parts; // buffer* - parts.push_back(M::LLVM::LLVMType::get( - boxchar.getContext(), L::Type::getIntNTy(llvmCtx, 64))); + parts.push_back(M::LLVM::LLVMType::get(boxchar.getContext(), + L::Type::getIntNTy(llvmCtx, 64))); // ... assert(false); - return t; // fixme + return t; // fixme } if (auto boxproc = t.dyn_cast()) { // (function*, host-context*) assert(false); - return t; // fixme + return t; // fixme } if (auto chr = t.dyn_cast()) { L::Type *llTy = L::Type::getIntNTy(llvmCtx, chr.getSizeInBits()); @@ -142,21 +149,22 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { } if (auto derived = t.dyn_cast()) { assert(false); - return t; // fixme + return t; // fixme } if (auto dims = t.dyn_cast()) { // [rank x ] L::Type *i64 = L::Type::getIntNTy(llvmCtx, 64); if (auto rank = dims.getRank()) { - return M::LLVM::LLVMType::get(dims.getContext(), + return M::LLVM::LLVMType::get( + dims.getContext(), L::ArrayType::get(L::VectorType::get(i64, 3), rank)); } - return M::LLVM::LLVMType::get( - dims.getContext(), L::VectorType::get(i64, 3)->getPointerTo()); + return M::LLVM::LLVMType::get(dims.getContext(), + L::VectorType::get(i64, 3)->getPointerTo()); } if (auto field = t.dyn_cast()) { - return M::LLVM::LLVMType::get( - field.getContext(), L::Type::getIntNTy(llvmCtx, 64)); + return M::LLVM::LLVMType::get(field.getContext(), + L::Type::getIntNTy(llvmCtx, 64)); } if (auto heap = t.dyn_cast()) { return convertPointerLike(heap); @@ -184,10 +192,10 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { M::Type eleTy = convertType(seq.getEleTy()); auto shape = seq.getShape(); L::SmallVector memshape; - if (shape.known) { - for (auto bi : shape.bounds) { - if (bi.known) { - memshape.push_back(bi.bound); + if (shape.hasValue()) { + for (auto bi : *shape) { + if (bi.hasValue()) { + memshape.push_back(*bi); } else { memshape.push_back(-1); // unknown shape } @@ -205,12 +213,13 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { }; /// FIR conversion pattern template -template class FIROpConversion : public M::ConversionPattern { +template +class FIROpConversion : public M::ConversionPattern { public: - explicit FIROpConversion( - M::MLIRContext *ctx, FIRToLLVMTypeConverter &lowering) - : ConversionPattern(FromOp::getOperationName(), 1, ctx), - lowering(lowering) {} + explicit FIROpConversion(M::MLIRContext *ctx, + FIRToLLVMTypeConverter &lowering) + : ConversionPattern(FromOp::getOperationName(), 1, ctx), + lowering(lowering) {} protected: L::LLVMContext &getLLVMContext() const { return lowering.getLLVMContext(); } @@ -223,8 +232,9 @@ template class FIROpConversion : public M::ConversionPattern { struct AllocaOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { auto alloc = M::cast(op); rewriter.replaceOpWithNewOp( op, lowering.convertType(alloc.getType()), operands, alloc.getAttrs()); @@ -236,8 +246,9 @@ struct AllocaOpConversion : public FIROpConversion { struct AllocMemOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { auto heap = M::cast(op); // FIXME: should be a call to malloc rewriter.replaceOpWithNewOp( @@ -250,8 +261,9 @@ struct AllocMemOpConversion : public FIROpConversion { struct ConvertOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { auto convert = M::cast(op); M::Type fromTy = lowering.convertType(convert.value()->getType()); M::Type toTy = lowering.convertType(convert.res()->getType()); @@ -320,8 +332,9 @@ struct ConvertOpConversion : public FIROpConversion { struct CoordinateOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { auto coor = M::cast(op); auto baseOp = coor.ref()->getDefiningOp(); auto loc = coor.getLoc(); @@ -332,7 +345,7 @@ struct CoordinateOpConversion : public FIROpConversion { rewriter.replaceOp(op, v); return matchSuccess(); } - assert(false); // FIXME + assert(false); // FIXME return matchSuccess(); } }; @@ -342,16 +355,17 @@ struct EmboxOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; M::Value *insertValue(M::Location loc, int index, M::Value *aggr, - M::Value *op, M::Type partTy, - M::ConversionPatternRewriter &rewriter) const { + M::Value *op, M::Type partTy, + M::ConversionPatternRewriter &rewriter) const { L::SmallVector attrs; attrs.push_back(rewriter.getI64IntegerAttr(index)); return rewriter.create( loc, partTy, aggr, op, rewriter.getArrayAttr(attrs)); } - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { auto embox = M::cast(op); auto *ctx = embox.getContext(); M::Type convTy = @@ -366,8 +380,8 @@ struct EmboxOpConversion : public FIROpConversion { L::Type *boxLLVMType = L::StructType::get(getLLVMContext(), members); M::Type boxTy = M::LLVM::LLVMType::get(ctx, boxLLVMType); auto loc = embox.getLoc(); - M::Value *u = rewriter.create( - loc, boxTy, L::ArrayRef{}); + M::Value *u = rewriter.create(loc, boxTy, + L::ArrayRef{}); auto *v = insertValue(loc, 0, u, operands[0], boxTy, rewriter); M::Type dimTy = M::LLVM::LLVMType::get(ctx, dimLLVMType); auto *w = insertValue(loc, 1, v, operands[1], dimTy, rewriter); @@ -379,8 +393,9 @@ struct EmboxOpConversion : public FIROpConversion { struct ExtractValueOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { auto extractVal = M::cast(op); // FIXME assert(false); @@ -395,8 +410,8 @@ struct FreeMemOpConversion : public FIROpConversion { return M::LLVM::LLVMType::getInt8PtrTy(getDialect()); } - M::FuncOp genFreeFunc( - M::Operation *op, M::ConversionPatternRewriter &rewriter) const { + M::FuncOp genFreeFunc(M::Operation *op, + M::ConversionPatternRewriter &rewriter) const { M::FuncOp freeFunc = op->getParentOfType().lookupSymbol("free"); if (!freeFunc) { @@ -407,14 +422,16 @@ struct FreeMemOpConversion : public FIROpConversion { return freeFunc; } - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { auto freemem = M::cast(op); M::FuncOp freeFunc = genFreeFunc(op, rewriter); M::Value *casted = rewriter.create( op->getLoc(), getVoidPtrType(), operands[0]); - rewriter.replaceOpWithNewOp(op, llvm::ArrayRef(), - rewriter.getSymbolRefAttr(freeFunc), casted); + rewriter.replaceOpWithNewOp( + op, llvm::ArrayRef(), rewriter.getSymbolRefAttr(freeFunc), + casted); return matchSuccess(); } }; @@ -423,8 +440,9 @@ struct GenDimsOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; // gendims(args:index, ...) ==> %v = ... : [size x <3 x index>] - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { auto argsize = operands.size(); assert(argsize % 3 == 0 && "must be a multiple of 3"); auto loc = op->getLoc(); @@ -436,8 +454,8 @@ struct GenDimsOpConversion : public FIROpConversion { L::Type *av3i64 = L::ArrayType::get(v3i64, argsize / 3); M::Type arrvec3 = M::LLVM::LLVMType::get(ctx, av3i64); auto i32Type = M::LLVM::LLVMType::get(ctx, L::Type::getIntNTy(llvmCtx, 32)); - M::Value *v = rewriter.create( - loc, arrvec3, L::ArrayRef{}); + M::Value *v = rewriter.create(loc, arrvec3, + L::ArrayRef{}); // BUG? insertelement requires an i32 for 3rd argument M::Value *zero = rewriter.create( loc, i32Type, rewriter.getI64IntegerAttr(0)); @@ -448,16 +466,16 @@ struct GenDimsOpConversion : public FIROpConversion { unsigned rank = 0; for (std::size_t i = 0; i < argsize;) { auto a1 = rewriter.create(loc, vec3); - auto a2 = rewriter.create( - loc, vec3, a1, operands[i++], zero); - auto a3 = rewriter.create( - loc, vec3, a2, operands[i++], one); - auto a4 = rewriter.create( - loc, vec3, a3, operands[i++], two); + auto a2 = rewriter.create(loc, vec3, a1, + operands[i++], zero); + auto a3 = rewriter.create(loc, vec3, a2, + operands[i++], one); + auto a4 = rewriter.create(loc, vec3, a3, + operands[i++], two); L::SmallVector attrs; attrs.push_back(rewriter.getI64IntegerAttr(rank++)); - v = rewriter.create( - loc, arrvec3, v, a4, rewriter.getArrayAttr(attrs)); + v = rewriter.create(loc, arrvec3, v, a4, + rewriter.getArrayAttr(attrs)); } rewriter.replaceOp(op, v); return matchSuccess(); @@ -481,8 +499,9 @@ class GlobalExprConversion : public FIROpConversion { struct InsertValueOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { auto insertVal = cast(op); // FIXME assert(false); @@ -494,11 +513,13 @@ struct InsertValueOpConversion : public FIROpConversion { struct LoadExprConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { auto load = M::cast(op); - auto newLoad = rewriter.create(load.getLoc(), - lowering.convertType(load.getType()), operands, load.getAttrs()); + auto newLoad = rewriter.create( + load.getLoc(), lowering.convertType(load.getType()), operands, + load.getAttrs()); // ???: the next line works around a bug [do we still need this?] load.replaceAllUsesWith(newLoad.getResult()); rewriter.replaceOp(op, newLoad.getResult()); @@ -509,8 +530,9 @@ struct LoadExprConversion : public FIROpConversion { struct NoReassocOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { // FIXME assert(false); return matchSuccess(); @@ -521,9 +543,11 @@ struct NoReassocOpConversion : public FIROpConversion { struct SelectOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - L::ArrayRef destinations, L::ArrayRef destOperands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + L::ArrayRef destinations, + L::ArrayRef destOperands, + M::ConversionPatternRewriter &rewriter) const override { // FIXME assert(false); return matchSuccess(); @@ -533,9 +557,11 @@ struct SelectOpConversion : public FIROpConversion { struct SelectCaseOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - L::ArrayRef destinations, L::ArrayRef destOperands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + L::ArrayRef destinations, + L::ArrayRef destOperands, + M::ConversionPatternRewriter &rewriter) const override { // FIXME assert(false); return matchSuccess(); @@ -545,9 +571,11 @@ struct SelectCaseOpConversion : public FIROpConversion { struct SelectRankOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - L::ArrayRef destinations, L::ArrayRef destOperands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + L::ArrayRef destinations, + L::ArrayRef destOperands, + M::ConversionPatternRewriter &rewriter) const override { // FIXME assert(false); return matchSuccess(); @@ -557,9 +585,11 @@ struct SelectRankOpConversion : public FIROpConversion { struct SelectTypeOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - L::ArrayRef destinations, L::ArrayRef destOperands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + L::ArrayRef destinations, + L::ArrayRef destOperands, + M::ConversionPatternRewriter &rewriter) const override { // FIXME assert(false); return matchSuccess(); @@ -570,8 +600,9 @@ struct SelectTypeOpConversion : public FIROpConversion { struct StoreExprConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { rewriter.replaceOpWithNewOp(op, operands[0], operands[1]); return matchSuccess(); } @@ -581,8 +612,9 @@ struct StoreExprConversion : public FIROpConversion { struct UndefOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { auto undef = M::cast(op); M::Value *v{rewriter.create( undef.getLoc(), lowering.convertType(undef.getType()))}; @@ -595,9 +627,10 @@ struct UndefOpConversion : public FIROpConversion { struct UnreachableOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { - L::SmallVector destinations; // none + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + L::SmallVector destinations; // none L::SmallVector destOperands; // none rewriter.create( op->getLoc(), operands, destinations, destOperands, op->getAttrs()); @@ -608,8 +641,9 @@ struct UnreachableOpConversion : public FIROpConversion { // Lower a SELECT operation into a cascade of conditional branches. The last // case must be the `true` condition. inline void rewriteSelectConstruct(M::Operation *op, OperandTy operands, - L::ArrayRef dests, L::ArrayRef destOperands, - M::OpBuilder &rewriter) { + L::ArrayRef dests, + L::ArrayRef destOperands, + M::OpBuilder &rewriter) { L::SmallVector noargs; L::SmallVector blocks; auto loc{op->getLoc()}; @@ -621,12 +655,12 @@ inline void rewriteSelectConstruct(M::Operation *op, OperandTy operands, rewriter.create(loc, dests[0], destOperands[0]); return; } - rewriter.create( - loc, operands[1], dests[0], destOperands[0], blocks[1], noargs); + rewriter.create(loc, operands[1], dests[0], destOperands[0], + blocks[1], noargs); for (std::size_t i = 1; i < dests.size() - 1; ++i) { rewriter.setInsertionPointToEnd(blocks[i]); - rewriter.create( - loc, operands[i + 1], dests[i], destOperands[i], blocks[i + 1], noargs); + rewriter.create(loc, operands[i + 1], dests[i], + destOperands[i], blocks[i + 1], noargs); } std::size_t last{dests.size() - 1}; rewriter.setInsertionPointToEnd(blocks[last]); @@ -638,8 +672,6 @@ inline void rewriteSelectConstruct(M::Operation *op, OperandTy operands, /// This pass lowers all FIR dialect operations to LLVM IR dialect. An /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. class FIRToLLVMLoweringPass : public M::ModulePass { - M::OpBuilder *builder; - public: void runOnModule() override { auto &context{getContext()}; @@ -647,10 +679,10 @@ class FIRToLLVMLoweringPass : public M::ModulePass { M::OwningRewritePatternList patterns; patterns .insert( - &context, typeConverter); + CoordinateOpConversion, EmboxOpConversion, FreeMemOpConversion, + GenDimsOpConversion, LoadExprConversion, NoReassocOpConversion, + StoreExprConversion, UndefOpConversion, + UnreachableOpConversion>(&context, typeConverter); M::populateStdToLLVMConversionPatterns(typeConverter, patterns); M::populateFuncOpTypeConversionPattern(patterns, &context, typeConverter); M::ConversionTarget target{context}; @@ -668,7 +700,7 @@ class FIRToLLVMLoweringPass : public M::ModulePass { if (M::failed(M::applyFullConversion( getModule(), target, std::move(patterns), &typeConverter))) { M::emitError(M::UnknownLoc::get(&context), - "error in converting to LLVM-IR dialect\n"); + "error in converting to LLVM-IR dialect\n"); signalPassFailure(); } } @@ -689,7 +721,7 @@ struct LLVMIRLoweringPass : public M::ModulePass { } }; -} // namespace +} // namespace std::unique_ptr fir::createFIRToLLVMPass() { return std::make_unique(); diff --git a/lib/fir/MemToReg.cpp b/lib/fir/MemToReg.cpp index 4cbbaa21ffc5..1f00777d8d7c 100644 --- a/lib/fir/MemToReg.cpp +++ b/lib/fir/MemToReg.cpp @@ -13,14 +13,14 @@ // limitations under the License. #include "fir/Transforms/MemToReg.h" +#include "fir/Analysis/IteratedDominanceFrontier.h" #include "fir/Dialect.h" #include "fir/FIROps.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/SmallVector.h" #include "mlir/Analysis/Dominance.h" -#include "mlir/Analysis/IteratedDominanceFrontier.h" #include "mlir/Dialect/StandardOps/Ops.h" #include "mlir/Pass/Pass.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" #include #include @@ -102,7 +102,7 @@ struct RenamePassData { using ValVector = std::vector; RenamePassData(M::Block *b, M::Block *p, const ValVector &v) - : BB(b), Pred(p), Values(v) {} + : BB(b), Pred(p), Values(v) {} RenamePassData(const RenamePassData &) = delete; RenamePassData &operator=(const RenamePassData &) = delete; RenamePassData(RenamePassData &&) = default; @@ -128,7 +128,8 @@ struct LargeBlockInfo { return false; } - template unsigned getInstructionIndex(A &op) { + template + unsigned getInstructionIndex(A &op) { auto *oper = op.getOperation(); // has it already been numbered? @@ -149,7 +150,8 @@ struct LargeBlockInfo { return it->second; } - template void deleteValue(A &op) { + template + void deleteValue(A &op) { auto *oper = op.getOperation(); instNumbers.erase(oper); } @@ -177,8 +179,8 @@ struct MemToReg : public M::FunctionPass { llvm::DenseMap, unsigned> BlockArgs; llvm::DenseMap, unsigned> argToAllocaMap; - bool rewriteSingleStoreAlloca( - fir::AllocaOp &AI, AllocaInfo &Info, LargeBlockInfo &LBI) { + bool rewriteSingleStoreAlloca(fir::AllocaOp &AI, AllocaInfo &Info, + LargeBlockInfo &LBI) { fir::StoreOp onlyStore(M::cast(Info.onlyStore)); M::Block *StoreBB = Info.onlyStore->getBlock(); int StoreIndex = -1; @@ -236,7 +238,7 @@ struct MemToReg : public M::FunctionPass { // Finally, after the scan, check to see if the store is all that is left. if (!Info.usingBlocks.empty()) - return false; // If not, we'll have to fall back for the remainder. + return false; // If not, we'll have to fall back for the remainder. // Remove the (now dead) store and alloca. Info.onlyStore->erase(); @@ -245,8 +247,8 @@ struct MemToReg : public M::FunctionPass { return true; } - bool promoteSingleBlockAlloca( - fir::AllocaOp &AI, AllocaInfo &Info, LargeBlockInfo &LBI) { + bool promoteSingleBlockAlloca(fir::AllocaOp &AI, AllocaInfo &Info, + LargeBlockInfo &LBI) { // Walk the use-def list of the alloca, getting the locations of all stores. using StoresByIndexTy = llvm::SmallVector, 64>; @@ -275,7 +277,8 @@ struct MemToReg : public M::FunctionPass { unsigned LoadIdx = LBI.getInstructionIndex(LI); // Find the nearest store that has a lower index than this load. - typename StoresByIndexTy::iterator I = llvm::lower_bound(StoresByIndex, + typename StoresByIndexTy::iterator I = llvm::lower_bound( + StoresByIndex, std::make_pair(LoadIdx, static_cast(nullptr)), llvm::less_first()); if (I == StoresByIndex.begin()) { @@ -324,8 +327,8 @@ struct MemToReg : public M::FunctionPass { } void computeLiveInBlocks(fir::AllocaOp &ae, AllocaInfo &Info, - const llvm::SmallPtrSetImpl &DefBlocks, - llvm::SmallPtrSetImpl &liveInBlks) { + const llvm::SmallPtrSetImpl &DefBlocks, + llvm::SmallPtrSetImpl &liveInBlks) { auto *AI = ae.getOperation(); // To determine liveness, we must iterate through the predecessors of blocks // where the def is live. Blocks are added to the worklist if we need to @@ -395,8 +398,8 @@ struct MemToReg : public M::FunctionPass { } } - bool addBlockArgument( - M::Block *BB, fir::AllocaOp &Alloca, unsigned allocaNum) { + bool addBlockArgument(M::Block *BB, fir::AllocaOp &Alloca, + unsigned allocaNum) { auto *ae = Alloca.getOperation(); auto key = std::make_pair(BB, ae); auto argNoIter = BlockArgs.find(key); @@ -410,16 +413,18 @@ struct MemToReg : public M::FunctionPass { return true; } - template + template void initOperands(std::vector &opers, M::Location &&loc, - M::Block *dest, unsigned size, unsigned ai, M::Value *val, A &&oldOpers) { + M::Block *dest, unsigned size, unsigned ai, M::Value *val, + A &&oldOpers) { unsigned i = 0; for (auto v : oldOpers) { opers[i++] = v; } // we must fill additional args with temporary undef values for (; i < size; ++i) { - if (i == ai) continue; + if (i == ai) + continue; auto opTy = dest->getArgument(i)->getType(); auto typedUndef = builder->create(loc, opTy); opers[i] = typedUndef; @@ -435,7 +440,7 @@ struct MemToReg : public M::FunctionPass { /// Set the incoming value on the branch side for the `ai`th block argument void setParam(M::Block *blk, unsigned ai, M::Value *val, M::Block *target, - unsigned size) { + unsigned size) { auto *term = blk->getTerminator(); if (auto br = M::dyn_cast(term)) { if (br.getNumOperands() <= ai) { @@ -459,13 +464,13 @@ struct MemToReg : public M::FunctionPass { auto *dest = cond.getTrueDest(); builder->setInsertionPoint(term); initOperands(opers, cond.getLoc(), dest, size, ai, val, - cond.getTrueOperands()); + cond.getTrueOperands()); auto *c = cond.getCondition(); auto *othDest = cond.getFalseDest(); - std::vector othOpers( - cond.false_operand_begin(), cond.false_operand_end()); - builder->create( - cond.getLoc(), c, dest, opers, othDest, othOpers); + std::vector othOpers(cond.false_operand_begin(), + cond.false_operand_end()); + builder->create(cond.getLoc(), c, dest, opers, + othDest, othOpers); cond.erase(); } else { auto *oldParam = cond.getTrueOperand(ai); @@ -479,13 +484,13 @@ struct MemToReg : public M::FunctionPass { auto *dest = cond.getFalseDest(); builder->setInsertionPoint(term); initOperands(opers, cond.getLoc(), dest, size, ai, val, - cond.getFalseOperands()); + cond.getFalseOperands()); auto *c = cond.getCondition(); auto *othDest = cond.getTrueDest(); - std::vector othOpers( - cond.true_operand_begin(), cond.true_operand_end()); - builder->create( - cond.getLoc(), c, othDest, othOpers, dest, opers); + std::vector othOpers(cond.true_operand_begin(), + cond.true_operand_end()); + builder->create(cond.getLoc(), c, othDest, othOpers, + dest, opers); cond.erase(); } else { auto *oldParam = cond.getFalseOperand(ai); @@ -499,7 +504,8 @@ struct MemToReg : public M::FunctionPass { } inline static void addValue(RenamePassData::ValVector &vector, - RenamePassData::ValVector::size_type size, M::Value *value) { + RenamePassData::ValVector::size_type size, + M::Value *value) { if (vector.size() < size + 1) { vector.resize(size + 1); } @@ -512,8 +518,8 @@ struct MemToReg : public M::FunctionPass { /// IncomingVals indicates what value each Alloca contains on exit from the /// predecessor block Pred. void renamePass(M::Block *BB, M::Block *Pred, - RenamePassData::ValVector &IncomingVals, - std::vector &Worklist) { + RenamePassData::ValVector &IncomingVals, + std::vector &Worklist) { NextIteration: // Does this block take arguments? if ((!BB->hasNoPredecessors()) && (BB->getNumArguments() > 0)) { @@ -534,7 +540,8 @@ struct MemToReg : public M::FunctionPass { M::Block::iterator II = BB->begin(); while (true) { - if (II == BB->end()) break; + if (II == BB->end()) + break; M::Operation &opn = *II; II++; @@ -613,7 +620,7 @@ struct MemToReg : public M::FunctionPass { std::vector aes; AllocaInfo info; LargeBlockInfo lbi; - M::ForwardIDFCalculator IDF(*domTree); + ForwardIDFCalculator IDF(*domTree); assert(!allocas.empty()); @@ -653,8 +660,8 @@ struct MemToReg : public M::FunctionPass { // insertion of dead phi nodes. // Unique the set of defining blocks for efficient lookup. - llvm::SmallPtrSet DefBlocks( - info.definingBlocks.begin(), info.definingBlocks.end()); + llvm::SmallPtrSet DefBlocks(info.definingBlocks.begin(), + info.definingBlocks.end()); // Determine which blocks the value is live in. These are blocks which // lead to uses. @@ -690,8 +697,8 @@ struct MemToReg : public M::FunctionPass { // has not been stored yet. In this case, it will get this null value. RenamePassData::ValVector Values(allocas.size()); for (unsigned i = 0, e = allocas.size(); i != e; ++i) { - Values[i] = builder->create( - allocas[i].getLoc(), allocas[i].getAllocatedType()); + Values[i] = builder->create(allocas[i].getLoc(), + allocas[i].getAllocatedType()); } // Walks all basic blocks in the function performing the SSA rename @@ -753,7 +760,7 @@ struct MemToReg : public M::FunctionPass { } }; -} // namespace +} // namespace std::unique_ptr fir::createMemToRegPass() { return std::make_unique(); diff --git a/lib/fir/StdConverter.cpp b/lib/fir/StdConverter.cpp index 630259da3e9c..d4caae2457a0 100644 --- a/lib/fir/StdConverter.cpp +++ b/lib/fir/StdConverter.cpp @@ -16,13 +16,6 @@ #include "fir/Dialect.h" #include "fir/FIROps.h" #include "fir/Type.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/Config/abi-breaking.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Type.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/raw_ostream.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h" #include "mlir/Dialect/AffineOps/AffineOps.h" @@ -33,6 +26,13 @@ #include "mlir/Target/LLVMIR.h" #include "mlir/Transforms/DialectConversion.h" #include "mlir/Transforms/LowerAffine.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Config/abi-breaking.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" // This module performs the conversion of FIR operations to MLIR standard and/or // LLVM-IR dialects. @@ -57,10 +57,14 @@ class FIRToStdTypeConverter : public M::TypeConverter { // convert a front-end kind value to either a std dialect type static M::Type kindToRealType(M::MLIRContext *ctx, KindTy kind) { switch (kind) { - case 2: return M::FloatType::getF16(ctx); - case 3: return M::FloatType::getBF16(ctx); - case 4: return M::FloatType::getF32(ctx); - case 8: return M::FloatType::getF64(ctx); + case 2: + return M::FloatType::getF16(ctx); + case 3: + return M::FloatType::getBF16(ctx); + case 4: + return M::FloatType::getF32(ctx); + case 8: + return M::FloatType::getF64(ctx); } return fir::RealType::get(ctx, kind); } @@ -84,8 +88,9 @@ class FIRToStdTypeConverter : public M::TypeConverter { // Lower a SELECT operation into a cascade of conditional branches. The last // case must be the `true` condition. inline void rewriteSelectConstruct(M::Operation *op, OperandTy operands, - L::ArrayRef dests, L::ArrayRef destOperands, - M::OpBuilder &rewriter) { + L::ArrayRef dests, + L::ArrayRef destOperands, + M::OpBuilder &rewriter) { L::SmallVector noargs; L::SmallVector blocks; auto loc{op->getLoc()}; @@ -97,12 +102,12 @@ inline void rewriteSelectConstruct(M::Operation *op, OperandTy operands, rewriter.create(loc, dests[0], destOperands[0]); return; } - rewriter.create( - loc, operands[1], dests[0], destOperands[0], blocks[1], noargs); + rewriter.create(loc, operands[1], dests[0], destOperands[0], + blocks[1], noargs); for (std::size_t i = 1; i < dests.size() - 1; ++i) { rewriter.setInsertionPointToEnd(blocks[i]); - rewriter.create( - loc, operands[i + 1], dests[i], destOperands[i], blocks[i + 1], noargs); + rewriter.create(loc, operands[i + 1], dests[i], + destOperands[i], blocks[i + 1], noargs); } std::size_t last{dests.size() - 1}; rewriter.setInsertionPointToEnd(blocks[last]); @@ -117,8 +122,8 @@ class FIRToStdLoweringPass : public M::ModulePass { if (M::dyn_cast(op) || M::dyn_cast(op) || M::dyn_cast(op)) { // build the lists of operands and successors - L::SmallVector operands{ - op->operand_begin(), op->operand_end()}; + L::SmallVector operands{op->operand_begin(), + op->operand_end()}; L::SmallVector destinations; destinations.reserve(op->getNumSuccessors()); L::SmallVector destOperands; @@ -131,8 +136,8 @@ class FIRToStdLoweringPass : public M::ModulePass { seen += n; } // do the rewrite - rewriteSelectConstruct(op, - L::makeArrayRef(operands.data(), operands.data() + firstSuccOpd), + rewriteSelectConstruct( + op, L::makeArrayRef(operands.data(), operands.data() + firstSuccOpd), destinations, destOperands, *builder); } } @@ -162,13 +167,13 @@ class FIRToStdLoweringPass : public M::ModulePass { if (M::failed(M::applyPartialConversion( getModule(), target, std::move(patterns), &typeConverter))) { M::emitError(M::UnknownLoc::get(&context), - "error in converting to standard dialect\n"); + "error in converting to standard dialect\n"); signalPassFailure(); } } }; -} // namespace +} // namespace std::unique_ptr fir::createFIRToStdPass() { return std::make_unique(); diff --git a/lib/fir/Type.cpp b/lib/fir/Type.cpp index 4122c7e4714e..a8b18d7142cc 100644 --- a/lib/fir/Type.cpp +++ b/lib/fir/Type.cpp @@ -14,12 +14,13 @@ #include "fir/Type.h" #include "fir/Dialect.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" #include "mlir/IR/Diagnostics.h" #include "mlir/IR/Dialect.h" #include "mlir/IR/StandardTypes.h" #include "mlir/Parser.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/Twine.h" namespace L = llvm; namespace M = mlir; @@ -68,28 +69,89 @@ class Lexer { // the token. Token lexToken(); + int nextNonWSChar() { + skipWhitespace(); + if (atEnd()) + return -1; + return *srcPtr++; + } + // peek ahead to the next non-whitespace character, leaving it on the input // stream char nextChar() { skipWhitespace(); - if (atEnd()) { + if (atEnd()) return '\0'; - } return *srcPtr; } // advance the input stream `count` characters void advance(unsigned count = 1) { while (count--) { - if (atEnd()) { + if (atEnd()) break; - } ++srcPtr; } } const char *getMarker() { return srcPtr; } + L::StringRef getNextType() { + char const *const marker = srcPtr; + L::SmallVector nestedPunc; + for (bool scanning = true; scanning && !atEnd(); ++srcPtr) { + char c = *srcPtr; + switch (c) { + case '<': + case '[': + case '(': + case '{': + nestedPunc.push_back(c); + break; + case '>': + if (nestedPunc.empty() || nestedPunc.pop_back_val() != '<') { + goto done; + } + continue; + case ']': + if (nestedPunc.empty() || nestedPunc.pop_back_val() != '[') { + goto done; + } + continue; + case ')': + if (nestedPunc.empty() || nestedPunc.pop_back_val() != '(') { + goto done; + } + continue; + case '}': + if (nestedPunc.empty() || nestedPunc.pop_back_val() != '{') { + goto done; + } + continue; + case ',': + if (nestedPunc.empty()) { + goto done; + } + continue; + case '-': + if ((srcPtr + 1 != srcBuff.end()) && *(srcPtr + 1) == '>') { + ++srcPtr; + } + continue; + done: + --srcPtr; + [[fallthrough]]; + case '\0': { + scanning = false; + } break; + default: + break; + } + } + std::size_t count = srcPtr - marker; + return {marker, count}; + } + private: void skipWhitespace() { while (!atEnd()) { @@ -99,8 +161,11 @@ class Lexer { case '\n': case '\r': case '\t': - case '\v': ++srcPtr; continue; - default: break; + case '\v': + ++srcPtr; + continue; + default: + break; } break; } @@ -132,28 +197,44 @@ Token Lexer::lexToken() { const char *tokStart = srcPtr; switch (*srcPtr++) { - case '<': return formToken(TokenKind::leftang, tokStart); - case '>': return formToken(TokenKind::rightang, tokStart); - case '{': return formToken(TokenKind::leftbrace, tokStart); - case '}': return formToken(TokenKind::rightbrace, tokStart); - case '[': return formToken(TokenKind::leftbracket, tokStart); - case ']': return formToken(TokenKind::rightbracket, tokStart); - case '(': return formToken(TokenKind::leftparen, tokStart); - case ')': return formToken(TokenKind::rightparen, tokStart); - case ':': return formToken(TokenKind::colon, tokStart); - case ',': return formToken(TokenKind::comma, tokStart); - case '"': return lexString(tokStart + 1); + case '<': + return formToken(TokenKind::leftang, tokStart); + case '>': + return formToken(TokenKind::rightang, tokStart); + case '{': + return formToken(TokenKind::leftbrace, tokStart); + case '}': + return formToken(TokenKind::rightbrace, tokStart); + case '[': + return formToken(TokenKind::leftbracket, tokStart); + case ']': + return formToken(TokenKind::rightbracket, tokStart); + case '(': + return formToken(TokenKind::leftparen, tokStart); + case ')': + return formToken(TokenKind::rightparen, tokStart); + case ':': + return formToken(TokenKind::colon, tokStart); + case ',': + return formToken(TokenKind::comma, tokStart); + case '"': + return lexString(tokStart + 1); case '-': if (*srcPtr == '>') { srcPtr++; return formToken(TokenKind::arrow, tokStart); } return lexNumber(tokStart); - case '+': return lexNumber(tokStart + 1); - case '!': return formToken(TokenKind::ecphoneme, tokStart); - case '?': return formToken(TokenKind::eroteme, tokStart); - case '*': return formToken(TokenKind::star, tokStart); - case '.': return formToken(TokenKind::period, tokStart); + case '+': + return lexNumber(tokStart + 1); + case '!': + return formToken(TokenKind::ecphoneme, tokStart); + case '?': + return formToken(TokenKind::eroteme, tokStart); + case '*': + return formToken(TokenKind::star, tokStart); + case '.': + return formToken(TokenKind::period, tokStart); default: if (std::isalpha(*tokStart)) { return lexIdent(tokStart); @@ -204,7 +285,7 @@ class auto_counter { class FIRTypeParser { public: FIRTypeParser(FIROpsDialect *dialect, L::StringRef rawData, M::Location loc) - : context{dialect->getContext()}, lexer{rawData}, loc{loc} {} + : context{dialect->getContext()}, lexer{rawData}, loc{loc} {} M::Type parseType(); @@ -217,7 +298,7 @@ class FIRTypeParser { auto token = lexer.lexToken(); if (token.kind != tk) { emitError(loc, msg); - return true; // error! + return true; // error! } return false; } @@ -226,13 +307,14 @@ class FIRTypeParser { auto lexCh = lexer.nextChar(); if (lexCh != ch) { emitError(loc, msg); - return true; // error! + return true; // error! } lexer.advance(); return false; } - template A parseIntLitSingleton(const char *msg) { + template + A parseIntLitSingleton(const char *msg) { if (consumeToken(TokenKind::leftang, "expected '<' in type")) { return {}; } @@ -249,22 +331,26 @@ class FIRTypeParser { if (consumeToken(TokenKind::rightang, "expected '>' in type")) { return {}; } - if (checkAtEnd()) return {}; + if (checkAtEnd()) + return {}; return A::get(getContext(), kind); } // `<` kind `>` - template A parseKindSingleton() { + template + A parseKindSingleton() { return parseIntLitSingleton("expected kind parameter"); } // `<` rank `>` - template A parseRankSingleton() { + template + A parseRankSingleton() { return parseIntLitSingleton("expected rank parameter"); } // '<' type '>' - template A parseTypeSingleton() { + template + A parseTypeSingleton() { if (consumeToken(TokenKind::leftang, "expected '<' in type")) { return {}; } @@ -276,12 +362,11 @@ class FIRTypeParser { if (consumeToken(TokenKind::rightang, "expected '>' in type")) { return {}; } - if (checkAtEnd()) return {}; + if (checkAtEnd()) + return {}; return A::get(ofTy); } - const char *advanceFuncType(); - std::pair advanceNonFuncType(const Token &lastTkn); M::Type parseNextType(); bool checkAtEnd() { @@ -295,8 +380,29 @@ class FIRTypeParser { return false; } - // `box` `<` type `>` - BoxType parseBox() { return parseTypeSingleton(); } + // `box` `<` type (',' affine-map)? `>` + BoxType parseBox() { + if (consumeToken(TokenKind::leftang, "expected '<' in type")) { + return {}; + } + auto ofTy = parseNextType(); + if (!ofTy) { + emitError(loc, "expected type parameter"); + return {}; + } + auto token = lexer.lexToken(); + if (token.kind == TokenKind::comma) { + // TODO: parse AffineMapAttr + token = lexer.lexToken(); + } + if (token.kind != TokenKind::rightang) { + emitError(loc, "expected '>' in type"); + return {}; + } + if (checkAtEnd()) + return {}; + return BoxType::get(ofTy); + } // `boxchar` `<` kind `>` BoxCharType parseBoxChar() { return parseKindSingleton(); } @@ -312,7 +418,8 @@ class FIRTypeParser { // `field` FieldType parseField() { - if (checkAtEnd()) return {}; + if (checkAtEnd()) + return {}; return FieldType::get(getContext()); } @@ -349,7 +456,8 @@ class FIRTypeParser { // `void` M::Type parseVoid() { - if (checkAtEnd()) return {}; + if (checkAtEnd()) + return {}; return M::TupleType::get(getContext()); } @@ -362,108 +470,10 @@ class FIRTypeParser { int recursiveCall{-1}; }; -const char *FIRTypeParser::advanceFuncType() { - Token token; - int lparen = 0; - while (true) { - token = lexer.lexToken(); - if (token.kind == TokenKind::leftparen) { - lparen++; - } else if (token.kind == TokenKind::rightparen) { - if (lparen == 0) break; - lparen--; - } - } - token = lexer.lexToken(); - if (token.kind != TokenKind::arrow) { - return lexer.getMarker(); - } - advanceNonFuncType(lexer.lexToken()); - return lexer.getMarker(); -} - -std::pair FIRTypeParser::advanceNonFuncType( - const Token &lastTkn) { - if (lastTkn.kind == TokenKind::ecphoneme) { // `!` - auto token = lexer.lexToken(); - if (token.kind == TokenKind::ident) { // `!` ident - if (token.text == "fir") { - token = lexer.lexToken(); - if (token.kind == TokenKind::period) { // `!fir.` - return {true, parseType()}; - } - } - char nextCh = lexer.nextChar(); - if (nextCh == '.') { // `!` ident `.` - lexer.lexToken(); // `.` - lexer.lexToken(); // ident - nextCh = lexer.nextChar(); - } - if (nextCh == '<') { // `!` ... `<` ... `>` - token = lexer.lexToken(); - int lang = 0; - while (true) { - token = lexer.lexToken(); - if (token.kind == TokenKind::leftang) { - lang++; - } else if (token.kind == TokenKind::rightang) { - if (lang == 0) break; - lang--; - } - } - } - } - } else if (lastTkn.kind == TokenKind::ident) { // ident - char nextCh = lexer.nextChar(); - if (nextCh == '<') { // ident `<` ... `>` - auto token = lexer.lexToken(); - int lang = 0; - while (true) { - token = lexer.lexToken(); - if (token.kind == TokenKind::leftang) { - lang++; - } else if (token.kind == TokenKind::rightang) { - if (lang == 0) break; - lang--; - } - } - } - } else if (lastTkn.kind == TokenKind::leftparen) { // `(` ... `)` - int lparen = 0; - while (true) { - auto token = lexer.lexToken(); - if (token.kind == TokenKind::leftparen) { - lparen++; - } else if (token.kind == TokenKind::rightparen) { - if (lparen == 0) break; - lparen--; - } - } - } else { - // This doesn't really look like a valid type. Let the standard type parser - // deal with it. - } - return {false, {}}; -} - // If this is a `!fir.x` type then recursively parse it now, otherwise figure // out its extent and call into the standard type parser. -// FIXME: advance by tracking matching pairs as in Parser.cpp M::Type FIRTypeParser::parseNextType() { - const char *marker = lexer.getMarker(); - Token token = lexer.lexToken(); - if (token.kind == TokenKind::leftparen) { - auto count = advanceFuncType() - marker; - assert(count > 0); - return M::parseType(L::StringRef(marker, count), getContext()); - } - auto pair = advanceNonFuncType(token); - if (pair.first) { - return pair.second; - } - auto count = lexer.getMarker() - marker; - assert(count > 0); - return M::parseType(L::StringRef(marker, count), getContext()); + return M::parseType(lexer.getNextType(), getContext()); } // Parses either `*` `:` @@ -471,6 +481,7 @@ M::Type FIRTypeParser::parseNextType() { SequenceType::Shape FIRTypeParser::parseShape() { SequenceType::Bounds bounds; int extent; + int nextChar; Token token = lexer.lexToken(); if (token.kind == TokenKind::star) { token = lexer.lexToken(); @@ -484,7 +495,7 @@ SequenceType::Shape FIRTypeParser::parseShape() { if (token.kind != TokenKind::eroteme) { goto shape_spec; } - bounds.emplace_back(false, 0); + bounds.emplace_back(SequenceType::Extent()); goto check_xchar; shape_spec: if (token.kind != TokenKind::intlit) { @@ -492,13 +503,13 @@ SequenceType::Shape FIRTypeParser::parseShape() { return {}; } token.text.getAsInteger(10, extent); - bounds.emplace_back(true, extent); + bounds.emplace_back(extent); check_xchar: - token = lexer.lexToken(); - if (token.kind == TokenKind::colon) { + nextChar = lexer.nextNonWSChar(); + if (nextChar == ':') { return SequenceType::Shape(bounds); } - if ((token.kind != TokenKind::ident) || (token.text != std::string("x"))) { + if (nextChar != 'x') { emitError(loc, "expected an 'x' or ':' after integer"); return {}; } @@ -507,7 +518,7 @@ SequenceType::Shape FIRTypeParser::parseShape() { } // bounds ::= lo extent stride | `?` -// `array` `<` bounds (`,` bounds)* `:` type `>` +// `array` `<` bounds (`,` bounds)* `:` type (',' affine-map)? `>` SequenceType FIRTypeParser::parseSequence() { if (consumeToken(TokenKind::leftang, "expected '<' in array type")) { return {}; @@ -518,7 +529,13 @@ SequenceType FIRTypeParser::parseSequence() { emitError(loc, "invalid element type"); return {}; } - if (consumeToken(TokenKind::rightang, "expected '>' in array type")) { + auto token = lexer.lexToken(); + if (token.kind == TokenKind::comma) { + // TODO: parse AffineMapAttr + token = lexer.lexToken(); + } + if (token.kind != TokenKind::rightang) { + emitError(loc, "expected '>' in array type"); return {}; } if (checkAtEnd()) { @@ -623,23 +640,40 @@ M::Type FIRTypeParser::parseType() { auto_counter c{recursiveCall}; auto token = lexer.lexToken(); if (token.kind == TokenKind::ident) { - if (token.text == "ref") return parseReference(); - if (token.text == "array") return parseSequence(); - if (token.text == "char") return parseCharacter(); - if (token.text == "logical") return parseLogical(); - if (token.text == "real") return parseReal(); - if (token.text == "type") return parseDerived(); - if (token.text == "box") return parseBox(); - if (token.text == "boxchar") return parseBoxChar(); - if (token.text == "boxproc") return parseBoxProc(); - if (token.text == "ptr") return parsePointer(); - if (token.text == "heap") return parseHeap(); - if (token.text == "dims") return parseDims(); - if (token.text == "tdesc") return parseTypeDesc(); - if (token.text == "field") return parseField(); - if (token.text == "int") return parseInteger(); - if (token.text == "complex") return parseComplex(); - if (token.text == "void") return parseVoid(); + if (token.text == "ref") + return parseReference(); + if (token.text == "array") + return parseSequence(); + if (token.text == "char") + return parseCharacter(); + if (token.text == "logical") + return parseLogical(); + if (token.text == "real") + return parseReal(); + if (token.text == "type") + return parseDerived(); + if (token.text == "box") + return parseBox(); + if (token.text == "boxchar") + return parseBoxChar(); + if (token.text == "boxproc") + return parseBoxProc(); + if (token.text == "ptr") + return parsePointer(); + if (token.text == "heap") + return parseHeap(); + if (token.text == "dims") + return parseDims(); + if (token.text == "tdesc") + return parseTypeDesc(); + if (token.text == "field") + return parseField(); + if (token.text == "int") + return parseInteger(); + if (token.text == "complex") + return parseComplex(); + if (token.text == "void") + return parseVoid(); emitError(loc, "not a known fir type"); return {}; } @@ -651,10 +685,10 @@ M::Type FIRTypeParser::parseType() { // is undefined and disallowed. bool singleIndirectionLevel(M::Type ty) { return !(ty.dyn_cast() || ty.dyn_cast() || - ty.dyn_cast()); + ty.dyn_cast()); } -} // anonymous +} // namespace namespace fir::detail { @@ -668,8 +702,8 @@ struct CharacterTypeStorage : public M::TypeStorage { bool operator==(const KeyTy &key) const { return key == getFKind(); } - static CharacterTypeStorage *construct( - M::TypeStorageAllocator &allocator, KindTy kind) { + static CharacterTypeStorage *construct(M::TypeStorageAllocator &allocator, + KindTy kind) { auto *storage = allocator.allocate(); return new (storage) CharacterTypeStorage{kind}; } @@ -693,8 +727,8 @@ struct DimsTypeStorage : public M::TypeStorage { return key == static_cast(getRank()); } - static DimsTypeStorage *construct( - M::TypeStorageAllocator &allocator, int rank) { + static DimsTypeStorage *construct(M::TypeStorageAllocator &allocator, + int rank) { auto *storage = allocator.allocate(); return new (storage) DimsTypeStorage{rank}; } @@ -717,8 +751,8 @@ struct FieldTypeStorage : public M::TypeStorage { bool operator==(const KeyTy &) const { return true; } - static FieldTypeStorage *construct( - M::TypeStorageAllocator &allocator, KindTy) { + static FieldTypeStorage *construct(M::TypeStorageAllocator &allocator, + KindTy) { auto *storage = allocator.allocate(); return new (storage) FieldTypeStorage{0}; } @@ -736,8 +770,8 @@ struct LogicalTypeStorage : public M::TypeStorage { bool operator==(const KeyTy &key) const { return key == getFKind(); } - static LogicalTypeStorage *construct( - M::TypeStorageAllocator &allocator, KindTy kind) { + static LogicalTypeStorage *construct(M::TypeStorageAllocator &allocator, + KindTy kind) { auto *storage = allocator.allocate(); return new (storage) LogicalTypeStorage{kind}; } @@ -760,8 +794,8 @@ struct IntTypeStorage : public M::TypeStorage { bool operator==(const KeyTy &key) const { return key == getFKind(); } - static IntTypeStorage *construct( - M::TypeStorageAllocator &allocator, KindTy kind) { + static IntTypeStorage *construct(M::TypeStorageAllocator &allocator, + KindTy kind) { auto *storage = allocator.allocate(); return new (storage) IntTypeStorage{kind}; } @@ -784,8 +818,8 @@ struct CplxTypeStorage : public M::TypeStorage { bool operator==(const KeyTy &key) const { return key == getFKind(); } - static CplxTypeStorage *construct( - M::TypeStorageAllocator &allocator, KindTy kind) { + static CplxTypeStorage *construct(M::TypeStorageAllocator &allocator, + KindTy kind) { auto *storage = allocator.allocate(); return new (storage) CplxTypeStorage{kind}; } @@ -808,8 +842,8 @@ struct RealTypeStorage : public M::TypeStorage { bool operator==(const KeyTy &key) const { return key == getFKind(); } - static RealTypeStorage *construct( - M::TypeStorageAllocator &allocator, KindTy kind) { + static RealTypeStorage *construct(M::TypeStorageAllocator &allocator, + KindTy kind) { auto *storage = allocator.allocate(); return new (storage) RealTypeStorage{kind}; } @@ -826,27 +860,37 @@ struct RealTypeStorage : public M::TypeStorage { /// Boxed object (a Fortran descriptor) struct BoxTypeStorage : public M::TypeStorage { - using KeyTy = M::Type; + using KeyTy = std::tuple; - static unsigned hashKey(const KeyTy &key) { return L::hash_combine(key); } + static unsigned hashKey(const KeyTy &key) { + auto hashVal{L::hash_combine(std::get(key))}; + return L::hash_combine(hashVal, + L::hash_combine(std::get(key))); + } - bool operator==(const KeyTy &key) const { return key == getElementType(); } + bool operator==(const KeyTy &key) const { + return std::get(key) == getElementType() && + std::get(key) == getLayoutMap(); + } - static BoxTypeStorage *construct( - M::TypeStorageAllocator &allocator, M::Type eleTy) { - assert(eleTy && "element type is null"); + static BoxTypeStorage *construct(M::TypeStorageAllocator &allocator, + const KeyTy &key) { auto *storage = allocator.allocate(); - return new (storage) BoxTypeStorage{eleTy}; + return new (storage) + BoxTypeStorage{std::get(key), std::get(key)}; } M::Type getElementType() const { return eleTy; } + M::AffineMapAttr getLayoutMap() const { return map; } protected: M::Type eleTy; + M::AffineMapAttr map; private: BoxTypeStorage() = delete; - explicit BoxTypeStorage(M::Type eleTy) : eleTy{eleTy} {} + explicit BoxTypeStorage(M::Type eleTy, M::AffineMapAttr map) + : eleTy{eleTy}, map{map} {} }; /// Boxed CHARACTER object type @@ -857,13 +901,13 @@ struct BoxCharTypeStorage : public M::TypeStorage { bool operator==(const KeyTy &key) const { return key == getFKind(); } - static BoxCharTypeStorage *construct( - M::TypeStorageAllocator &allocator, KindTy kind) { + static BoxCharTypeStorage *construct(M::TypeStorageAllocator &allocator, + KindTy kind) { auto *storage = allocator.allocate(); return new (storage) BoxCharTypeStorage{kind}; } - KindTy getFKind() const { return kind / 8; } + KindTy getFKind() const { return kind; } // a !fir.boxchar always wraps a !fir.char CharacterType getElementType(M::MLIRContext *ctxt) const { @@ -886,8 +930,8 @@ struct BoxProcTypeStorage : public M::TypeStorage { bool operator==(const KeyTy &key) const { return key == getElementType(); } - static BoxProcTypeStorage *construct( - M::TypeStorageAllocator &allocator, M::Type eleTy) { + static BoxProcTypeStorage *construct(M::TypeStorageAllocator &allocator, + M::Type eleTy) { assert(eleTy && "element type is null"); auto *storage = allocator.allocate(); return new (storage) BoxProcTypeStorage{eleTy}; @@ -911,8 +955,8 @@ struct ReferenceTypeStorage : public M::TypeStorage { bool operator==(const KeyTy &key) const { return key == getElementType(); } - static ReferenceTypeStorage *construct( - M::TypeStorageAllocator &allocator, M::Type eleTy) { + static ReferenceTypeStorage *construct(M::TypeStorageAllocator &allocator, + M::Type eleTy) { assert(eleTy && "element type is null"); auto *storage = allocator.allocate(); return new (storage) ReferenceTypeStorage{eleTy}; @@ -936,8 +980,8 @@ struct PointerTypeStorage : public M::TypeStorage { bool operator==(const KeyTy &key) const { return key == getElementType(); } - static PointerTypeStorage *construct( - M::TypeStorageAllocator &allocator, M::Type eleTy) { + static PointerTypeStorage *construct(M::TypeStorageAllocator &allocator, + M::Type eleTy) { assert(eleTy && "element type is null"); auto *storage = allocator.allocate(); return new (storage) PointerTypeStorage{eleTy}; @@ -961,8 +1005,8 @@ struct HeapTypeStorage : public M::TypeStorage { bool operator==(const KeyTy &key) const { return key == getElementType(); } - static HeapTypeStorage *construct( - M::TypeStorageAllocator &allocator, M::Type eleTy) { + static HeapTypeStorage *construct(M::TypeStorageAllocator &allocator, + M::Type eleTy) { assert(eleTy && "element type is null"); auto *storage = allocator.allocate(); return new (storage) HeapTypeStorage{eleTy}; @@ -980,41 +1024,46 @@ struct HeapTypeStorage : public M::TypeStorage { /// Sequence-like object storage struct SequenceTypeStorage : public M::TypeStorage { - using KeyTy = std::pair; + using KeyTy = std::tuple; static unsigned hashKey(const KeyTy &key) { auto shapeHash{hash_value(std::get(key))}; - return L::hash_combine(shapeHash, std::get(key)); + shapeHash = L::hash_combine(shapeHash, std::get(key)); + return L::hash_combine(shapeHash, std::get(key)); } bool operator==(const KeyTy &key) const { - return key == KeyTy{getShape(), getElementType()}; + return key == KeyTy{getShape(), getElementType(), getLayoutMap()}; } - static SequenceTypeStorage *construct( - M::TypeStorageAllocator &allocator, const KeyTy &key) { + static SequenceTypeStorage *construct(M::TypeStorageAllocator &allocator, + const KeyTy &key) { auto *storage = allocator.allocate(); - return new (storage) SequenceTypeStorage{key.first, key.second}; + return new (storage) SequenceTypeStorage{std::get(key), + std::get(key), + std::get(key)}; } SequenceType::Shape getShape() const { return shape; } M::Type getElementType() const { return eleTy; } + M::AffineMapAttr getLayoutMap() const { return map; } protected: SequenceType::Shape shape; M::Type eleTy; + M::AffineMapAttr map; private: SequenceTypeStorage() = delete; - explicit SequenceTypeStorage( - const SequenceType::Shape &shape, M::Type eleTy) - : shape{shape}, eleTy{eleTy} {} + explicit SequenceTypeStorage(const SequenceType::Shape &shape, M::Type eleTy, + M::AffineMapAttr map) + : shape{shape}, eleTy{eleTy}, map{map} {} }; /// Derived type storage struct RecordTypeStorage : public M::TypeStorage { using KeyTy = std::tuple, - L::ArrayRef>; + L::ArrayRef>; static unsigned hashKey(const KeyTy &key) { return L::hash_combine(std::get<0>(key).str()); @@ -1024,8 +1073,8 @@ struct RecordTypeStorage : public M::TypeStorage { return std::get<0>(key) == getName(); } - static RecordTypeStorage *construct( - M::TypeStorageAllocator &allocator, const KeyTy &key) { + static RecordTypeStorage *construct(M::TypeStorageAllocator &allocator, + const KeyTy &key) { auto *storage = allocator.allocate(); auto &name = std::get<0>(key); auto &lens = std::get<1>(key); @@ -1049,9 +1098,9 @@ struct RecordTypeStorage : public M::TypeStorage { private: RecordTypeStorage() = delete; explicit RecordTypeStorage(L::StringRef name, - L::ArrayRef lens, - L::ArrayRef types) - : name{name}, lens{lens}, types{types} {} + L::ArrayRef lens, + L::ArrayRef types) + : name{name}, lens{lens}, types{types} {} }; /// Type descriptor type storage @@ -1062,8 +1111,8 @@ struct TypeDescTypeStorage : public M::TypeStorage { bool operator==(const KeyTy &key) const { return key == getOfType(); } - static TypeDescTypeStorage *construct( - M::TypeStorageAllocator &allocator, M::Type ofTy) { + static TypeDescTypeStorage *construct(M::TypeStorageAllocator &allocator, + M::Type ofTy) { assert(ofTy && "descriptor type is null"); auto *storage = allocator.allocate(); return new (storage) TypeDescTypeStorage{ofTy}; @@ -1080,9 +1129,10 @@ struct TypeDescTypeStorage : public M::TypeStorage { explicit TypeDescTypeStorage(M::Type ofTy) : ofTy{ofTy} {} }; -} // detail +} // namespace fir::detail -template bool inbounds(A v, B lb, B ub) { +template +bool inbounds(A v, B lb, B ub) { return v >= lb && v < ub; } @@ -1091,8 +1141,8 @@ bool fir::isa_fir_type(mlir::Type t) { } bool fir::isa_std_type(mlir::Type t) { - return inbounds( - t.getKind(), M::Type::FIRST_STANDARD_TYPE, M::Type::LAST_STANDARD_TYPE); + return inbounds(t.getKind(), M::Type::FIRST_STANDARD_TYPE, + M::Type::LAST_STANDARD_TYPE); } bool fir::isa_fir_or_std_type(mlir::Type t) { @@ -1156,12 +1206,24 @@ int fir::RealType::getSizeInBits() const { return getImpl()->getFKind(); } // Box -BoxType fir::BoxType::get(M::Type elementType) { - return Base::get(elementType.getContext(), FIR_BOX, elementType); +BoxType fir::BoxType::get(M::Type elementType, M::AffineMapAttr map) { + return Base::get(elementType.getContext(), FIR_BOX, elementType, map); } M::Type fir::BoxType::getEleTy() const { return getImpl()->getElementType(); } +M::AffineMapAttr fir::BoxType::getLayoutMap() const { + return getImpl()->getLayoutMap(); +} + +M::LogicalResult +fir::BoxType::verifyConstructionInvariants(L::Optional, + M::MLIRContext *ctx, M::Type eleTy, + mlir::AffineMapAttr map) { + // TODO + return M::success(); +} + // BoxChar BoxCharType fir::BoxCharType::get(M::MLIRContext *ctxt, KindTy kind) { @@ -1182,6 +1244,13 @@ M::Type fir::BoxProcType::getEleTy() const { return getImpl()->getElementType(); } +M::LogicalResult fir::BoxProcType::verifyConstructionInvariants( + L::Optional loc, M::MLIRContext *context, M::Type eleTy) { + if (eleTy.dyn_cast() || eleTy.dyn_cast()) + return M::success(); + return M::failure(); +} + // Reference ReferenceType fir::ReferenceType::get(M::Type elementType) { @@ -1192,6 +1261,14 @@ M::Type fir::ReferenceType::getEleTy() const { return getImpl()->getElementType(); } +M::LogicalResult fir::ReferenceType::verifyConstructionInvariants( + L::Optional loc, M::MLIRContext *context, M::Type eleTy) { + if (eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast()) + return M::failure(); + return M::success(); +} + // Pointer PointerType fir::PointerType::get(M::Type elementType) { @@ -1206,6 +1283,17 @@ M::Type fir::PointerType::getEleTy() const { return getImpl()->getElementType(); } +M::LogicalResult fir::PointerType::verifyConstructionInvariants( + L::Optional loc, M::MLIRContext *context, M::Type eleTy) { + if (eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast()) + return M::failure(); + return M::success(); +} + // Heap HeapType fir::HeapType::get(M::Type elementType) { @@ -1218,40 +1306,69 @@ HeapType fir::HeapType::get(M::Type elementType) { M::Type fir::HeapType::getEleTy() const { return getImpl()->getElementType(); } +M::LogicalResult fir::HeapType::verifyConstructionInvariants( + L::Optional loc, M::MLIRContext *context, M::Type eleTy) { + if (eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast()) + return M::failure(); + return M::success(); +} + // Sequence -SequenceType fir::SequenceType::get(const Shape &shape, M::Type elementType) { +SequenceType fir::SequenceType::get(const Shape &shape, M::Type elementType, + M::AffineMapAttr map) { auto *ctxt = elementType.getContext(); - return Base::get(ctxt, FIR_SEQUENCE, shape, elementType); + return Base::get(ctxt, FIR_SEQUENCE, shape, elementType, map); } M::Type fir::SequenceType::getEleTy() const { return getImpl()->getElementType(); } +M::AffineMapAttr fir::SequenceType::getLayoutMap() const { + return getImpl()->getLayoutMap(); +} + SequenceType::Shape fir::SequenceType::getShape() const { return getImpl()->getShape(); } +M::LogicalResult fir::SequenceType::verifyConstructionInvariants( + L::Optional loc, M::MLIRContext *context, + const SequenceType::Shape &shape, M::Type eleTy, M::AffineMapAttr map) { + // DIMENSION attribute can only be applied to an intrinsic or record type + if (eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast()) + return M::failure(); + return M::success(); +} + // compare if two shapes are equivalent -bool fir::operator==( - const SequenceType::Shape &sh_1, const SequenceType::Shape &sh_2) { - if (sh_1.known != sh_2.known) { +bool fir::operator==(const SequenceType::Shape &sh_1, + const SequenceType::Shape &sh_2) { + if (sh_1.hasValue() != sh_2.hasValue()) { return false; } - if (!sh_1.known) { + if (!sh_1.hasValue()) { return true; } - auto &bnd_1 = sh_1.bounds; - auto &bnd_2 = sh_2.bounds; + auto &bnd_1 = *sh_1; + auto &bnd_2 = *sh_2; if (bnd_1.size() != bnd_2.size()) { return false; } for (std::size_t i = 0, end = bnd_1.size(); i != end; ++i) { - if (bnd_1[i].known != bnd_2[i].known) { + if (bnd_1[i].hasValue() != bnd_2[i].hasValue()) { return false; } - if (bnd_1[i].known && bnd_1[i].bound != bnd_2[i].bound) { + if (bnd_1[i].hasValue() && *bnd_1[i] != *bnd_2[i]) { return false; } } @@ -1260,21 +1377,24 @@ bool fir::operator==( // compute the hash of an Extent L::hash_code fir::hash_value(const SequenceType::Extent &ext) { - return L::hash_combine(ext.known ? ext.bound : 0); + return L::hash_combine(ext.hasValue() ? *ext : 0); } // compute the hash of a Shape L::hash_code fir::hash_value(const SequenceType::Shape &sh) { - if (sh.known) { - return L::hash_combine_range(sh.bounds.begin(), sh.bounds.end()); + if (sh.hasValue()) { + return L::hash_combine_range(sh->begin(), sh->end()); } return L::hash_combine(0); } -// Derived +/// RecordType +/// +/// This type captures a Fortran "derived type" RecordType fir::RecordType::get(M::MLIRContext *ctxt, L::StringRef name, - L::ArrayRef lenPList, L::ArrayRef typeList) { + L::ArrayRef lenPList, + L::ArrayRef typeList) { return Base::get(ctxt, FIR_DERIVED, name, lenPList, typeList); } @@ -1288,7 +1408,42 @@ RecordType::TypeList fir::RecordType::getLenParamList() { return getImpl()->getLenParamList(); } -// Type descriptor +inline static bool verifyIntegerType(M::Type ty) { + return ty.dyn_cast() || ty.dyn_cast(); +} + +inline static bool verifyRecordMemberType(M::Type ty) { + return !(ty.dyn_cast() || ty.dyn_cast() || + ty.dyn_cast() || ty.dyn_cast() || + ty.dyn_cast() || ty.dyn_cast() || + ty.dyn_cast()); +} + +M::LogicalResult fir::RecordType::verifyConstructionInvariants( + L::Optional, M::MLIRContext *context, L::StringRef name, + L::ArrayRef lenPList, + L::ArrayRef typeList) { + if (name.size() == 0) + return M::failure(); + llvm::StringSet uniq; + for (auto &p : lenPList) + if (!uniq.insert(p.first).second) + return M::failure(); + for (auto &p : typeList) + if (!uniq.insert(p.first).second) + return M::failure(); + for (auto &p : lenPList) + if (!verifyIntegerType(p.second)) + return M::failure(); + for (auto &p : typeList) + if (!verifyRecordMemberType(p.second)) + return M::failure(); + return M::success(); +} + +/// Type descriptor type +/// +/// This is the type of a type descriptor object (similar to a class instance) TypeDescType fir::TypeDescType::get(M::Type ofType) { assert(!ofType.dyn_cast()); @@ -1297,10 +1452,20 @@ TypeDescType fir::TypeDescType::get(M::Type ofType) { M::Type fir::TypeDescType::getOfTy() const { return getImpl()->getOfType(); } +M::LogicalResult fir::TypeDescType::verifyConstructionInvariants( + L::Optional loc, M::MLIRContext *context, M::Type eleTy) { + if (eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast()) + return M::failure(); + return M::success(); +} + // Implementation of the thin interface from dialect to type parser -M::Type fir::parseFirType( - FIROpsDialect *dialect, L::StringRef rawData, M::Location loc) { +M::Type fir::parseFirType(FIROpsDialect *dialect, L::StringRef rawData, + M::Location loc) { FIRTypeParser parser{dialect, rawData, loc}; return parser.parseType(); } diff --git a/test/fir/fir-ops.fir b/test/fir/fir-ops.fir index fd87c524e41a..f114346824d9 100644 --- a/test/fir/fir-ops.fir +++ b/test/fir/fir-ops.fir @@ -12,7 +12,7 @@ func @dvd3() -> !fir.type func @dvd4() -> !fir.type func @arr1() -> !fir.array<10:f32> -//func @arr2() -> !fir.array<10x10:f32> +func @arr2() -> !fir.array<10x10:f32> func @arr3() -> !fir.array func @arr4() -> !fir.array<10x?:f32> func @arr5() -> !fir.array @@ -26,11 +26,24 @@ func @mem4() -> !fir.ref<() -> ()> func @box1() -> !fir.box> func @box2() -> !fir.boxchar<2> func @box3() -> !fir.boxproc<(i32, i32) -> i64> +func @box4() -> !fir.box> func @oth1() -> !fir.dims<1> func @oth2() -> !fir.field func @oth3() -> !fir.tdesc> +func @print_index3(index, index, index) +func @user_i64(i64) +func @user_tdesc(!fir.tdesc>) +func @store_tuple(tuple) -> () + +func @get_method_box() -> !fir.box> +func @method_impl(!fir.box>) + +func @nop() + +func @get_func() -> (() -> ()) + func @instructions() { %0 = fir.alloca !fir.array<10:i32> : !fir.ref> %1 = fir.load %0 : !fir.ref> @@ -42,42 +55,185 @@ func @instructions() { %6 = fir.embox %5 : (!fir.heap>) -> !fir.box> %7 = fir.box_addr %6 : (!fir.box>) -> !fir.ref> %c0 = constant 0 : index -// %8 = fir.box_dims %6, %c0 : (!fir.box>) -> (index, index, index) -// %8 = fir.call @it1() : () -> !fir.int<4> - %8 = call @it1() : () -> !fir.int<4> + %d1:3 = fir.box_dims %6, %c0 : (!fir.box>, index) -> (index, index, index) + fir.call @print_index3(%d1#0, %d1#1, %d1#2) : (index, index, index) -> () + %8 = fir.call @it1() : () -> !fir.int<4> %9 = fir.box_elesize %6 : (!fir.box>) -> i64 %10 = fir.box_isalloc %6 : (!fir.box>) -> i1 %11 = fir.box_isarray %6 : (!fir.box>) -> i1 %12 = fir.box_isptr %6 : (!fir.box>) -> i1 %13 = fir.box_rank %6 : (!fir.box>) -> i64 %14 = fir.box_tdesc %6 : (!fir.box>) -> !fir.tdesc> -// %15 = call @box2() : () -> !fir.boxchar<2> - %15 = call @it1() : () -> !fir.int<4> -// %16 = fir.boxchar_len %15 : (!fir.boxchar<2>) -> i32 - %16 = call @it1() : () -> !fir.int<4> - %17 = call @box3() : () -> !fir.boxproc<(i32, i32) -> i64> -// %18 = fir.boxproc_host %17 : (!fir.boxproc<(i32, i32) -> i64>) -> (!fir.ref<(i32, i32) -> i64>, !fir.ref<(i32, f64)>) - %18,%19 = fir.boxproc_host %17 : (!fir.boxproc<(i32, i32) -> i64>) -> (!fir.ref<(i32, i32) -> i64>, !fir.ref) - %c10 = constant 10 : i32 - %20 = fir.coordinate_of %5, %c10 : (!fir.heap>, i32) -> !fir.ref -// %21 = fir.field_index "f" : () -> !fir.field - %21 = fir.undefined !fir.field -// %22 = call @dvd4() : () -> !fir.type + %15 = fir.call @box2() : () -> !fir.boxchar<2> + %16 = fir.boxchar_len %15 : (!fir.boxchar<2>) -> i32 + %17 = fir.call @box3() : () -> !fir.boxproc<(i32, i32) -> i64> + %18 = fir.boxproc_host %17 : (!fir.boxproc<(i32, i32) -> i64>) -> !fir.ref + %19 = constant 10 : i32 + %20 = fir.coordinate_of %5, %19 : (!fir.heap>, i32) -> !fir.ref + %21 = fir.field_index("f") : !fir.field %22 = fir.undefined !fir.type %23 = fir.extract_value %22, %21 : (!fir.type, !fir.field) -> f32 %c1 = constant 1 : i32 - %24 = fir.gendims %c1, %c10, %c1 : (i32, i32, i32) -> !fir.dims<1> + %24 = fir.gendims %c1, %19, %c1 : (i32, i32, i32) -> !fir.dims<1> %cf1 = constant 1.0 : f32 %25 = fir.insert_value %22, %cf1, %21 : (!fir.type, f32, !fir.field) -> !fir.type -// %26 = fir.len_param_index "f" : (!fir.field) -> i32 - %26 = fir.undefined i32 -// %27 = fir.call @dvd3() : () -> !fir.type - %27 = fir.undefined !fir.type -// %28 = fir.dispatch "method"(%27) : () -> i32 - %28 = fir.undefined i32 - %29 = fir.convert %26 : (i32) -> i64 + %26 = fir.len_param_index("f") : !fir.field + %27 = fir.call @box4() : () -> !fir.box> + %28 = fir.dispatch "method"(%27) : (!fir.box>) -> i32 + %29 = fir.convert %28 : (i32) -> i64 %30 = fir.gentypedesc !fir.type : !fir.tdesc> + fir.call @user_tdesc(%30) : (!fir.tdesc>) -> () %31 = fir.no_reassoc %29 : i64 + fir.call @user_i64(%31) : (i64) -> () fir.freemem %5 : !fir.heap> + %32 = fir.call @get_func() : () -> (() -> ()) + fir.icall %32() : () -> () + %33 = fir.address_of (@it1) : !fir.ref<() -> !fir.int<4>> + return +} + +func @boxing_match() { + %0 = fir.alloca i32 : !fir.ref + %1 = fir.embox %0 : (!fir.ref) -> !fir.box + %2:6 = fir.unbox %1 : (!fir.box) -> (!fir.ref,i32,i32,!fir.tdesc,i32,!fir.dims<0>) + %c8 = constant 8 : i32 + %3 = fir.alloca !fir.char<1> : !fir.ref> + %b3 = fir.undefined !fir.char<1> + %4 = fir.emboxchar %b3, %c8 : (!fir.char<1>, i32) -> !fir.boxchar<1> + %5:2 = fir.unboxchar %4 : (!fir.boxchar<1>) -> (!fir.ref>, i32) + %6 = fir.alloca tuple : !fir.ref> + %a1 = fir.undefined tuple + %z = constant 0 : i32 + %c12 = constant 12 : i32 + %a2 = fir.insert_value %a1, %c12, %z : (tuple, i32, i32) -> tuple + %z1 = constant 1 : i32 + %c42 = constant 42.13 : f64 + %a3 = fir.insert_value %a1, %c42, %z1 : (tuple, f64, i32) -> tuple + fir.store %a3 to %6 : !fir.ref> + %7 = fir.emboxproc @method_impl, %6 : ((!fir.box>) -> (), !fir.ref>) -> !fir.boxproc<(!fir.box>) -> ()> + %8:2 = fir.unboxproc %7 : (!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 } + +func @loop() { + %c1 = constant 1 : index + %c10 = constant 10 : index + %ct = constant true + fir.loop %i = %c1 to %c10 { + fir.where %ct { + fir.call @nop() : () -> () + } otherwise { + fir.call @nop() : () -> () + } + } + fir.unreachable +} + +func @bar_select(%arg : i32, %arg2 : i32) -> i32 { + %0 = constant 1 : i32 + %1 = constant 2 : i32 + %2 = constant 3 : i32 + %3 = constant 4 : i32 + 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 +} + +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 + 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 +} + +func @bar_select_type(%arg : !fir.box}>>) -> i32 { + %0 = constant 1 : i32 + %1 = constant 2 : i32 + %2 = constant 3 : i32 + %3 = constant 4 : i32 + 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 +} + +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 + 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 +} + +fir.global @global_var : i32 { + %0 = constant 1 : i32 +} + +fir.global @global_constant constant : i32 { + %0 = constant 934 : i32 +} + +fir.global @global_derived : !fir.type { + %0 = constant 1 : i32 + fir.global_entry "f", %0 : i32 +} + +fir.dispatch_table @dispatch_tbl { + fir.dt_entry "method", @method_impl +} diff --git a/test/fir/fir-types.fir b/test/fir/fir-types.fir new file mode 100644 index 000000000000..c61b7d98bde3 --- /dev/null +++ b/test/fir/fir-types.fir @@ -0,0 +1,38 @@ +// Test the FIR operations + +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> + +func @dvd1() -> !fir.type +func @dvd2() -> !fir.type +func @dvd3() -> !fir.type +func @dvd4() -> !fir.type +func @dvd5() -> !fir.type +func @dvd6() -> !fir.type>}> + +func @arr1() -> !fir.array<10:f32> +func @arr2() -> !fir.array<10x10:f32> +func @arr3() -> !fir.array +func @arr4() -> !fir.array<10x?:f32> +func @arr5() -> !fir.array +func @arr6() -> !fir.array<*:f32> +func @arr7() -> !fir.array<1x2x?x4x5x6x7x8x9:f32> + +func @mem1() -> !fir.ref +func @mem2() -> !fir.ptr +func @mem3() -> !fir.heap +func @mem4() -> !fir.ref<() -> ()> + +func @box1() -> !fir.box> +func @box2() -> !fir.boxchar<2> +func @box3() -> !fir.boxproc<(i32, i32) -> i64> +func @box4() -> !fir.box +//func @box5() -> !fir.box<()> + +func @oth1() -> !fir.dims<1> +func @oth2() -> !fir.field +func @oth3() -> !fir.tdesc> +func @oth4() -> !fir.dims<15> diff --git a/tools/fml/CMakeLists.txt b/tools/fml/CMakeLists.txt index 73a761c0a161..c30dcdc5dcab 100644 --- a/tools/fml/CMakeLists.txt +++ b/tools/fml/CMakeLists.txt @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L/home/eschweitz/clang-mlir/lib -fno-rtti") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-string-conversion") add_executable(fml fml.cc diff --git a/tools/fml/fml.cc b/tools/fml/fml.cc index f6f0fc15eb12..823dcddb378a 100644 --- a/tools/fml/fml.cc +++ b/tools/fml/fml.cc @@ -14,6 +14,12 @@ // Temporary Fortran front end driver main program for development scaffolding. +#include "fir/Dialect.h" +#include "fir/Tilikum/LLVMConverter.h" +#include "fir/Tilikum/StdConverter.h" +#include "fir/Transforms/MemToReg.h" +#include "../../lib/burnside/bridge.h" +#include "../../lib/burnside/convert-expr.h" #include "../../lib/common/default-kinds.h" #include "../../lib/parser/characters.h" #include "../../lib/parser/dump-parse-tree.h" @@ -37,12 +43,6 @@ #include "mlir/Pass/Pass.h" #include "mlir/Pass/PassManager.h" #include "mlir/Transforms/Passes.h" -#include "fir/Dialect.h" -#include "fir/Tilikum/LLVMConverter.h" -#include "fir/Tilikum/StdConverter.h" -#include "fir/Transforms/MemToReg.h" -#include "../../lib/burnside/bridge.h" -#include "../../lib/burnside/canonicalize.h" #include #include #include @@ -267,12 +267,13 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, } // MLIR+FIR - Br::instantiateBurnsideBridge(semanticsContext.defaultKinds()); + Br::instantiateBurnsideBridge( + semanticsContext.defaultKinds(), &parsing.cooked()); Br::crossBurnsideBridge(Br::getBridge(), parseTree); mlir::ModuleOp mlirModule{Br::getBridge().getModule()}; mlir::PassManager pm{mlirModule.getContext()}; if (driver.dumpHLFIR) { - llvm::outs() << ";== 1 ==\n"; + llvm::errs() << ";== 1 ==\n"; mlirModule.dump(); } pm.addPass(fir::createMemToRegPass()); @@ -289,7 +290,7 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, } auto result{pm.run(mlirModule)}; if (driver.dumpFIR) { - llvm::outs() << ";== 2 ==\n"; + llvm::errs() << ";== 2 ==\n"; mlirModule.dump(); } return {}; @@ -323,7 +324,7 @@ std::string CompileFir(std::string path, Fortran::parser::Options options, std::exit(4); } - llvm::outs() << ";== 3 ==\n"; + llvm::errs() << ";== 3 ==\n"; mlirModule.dump(); // run passes @@ -340,10 +341,10 @@ std::string CompileFir(std::string path, Fortran::parser::Options options, pm.addPass(fir::createLLVMDialectToLLVMPass()); } auto result{pm.run(mlirModule)}; - llvm::outs() << ";== 4 ==\n"; + llvm::errs() << ";== 4 ==\n"; mlirModule.dump(); if (mlir::succeeded(result)) { - llvm::outs() << "a.ll written\n"; + llvm::errs() << "a.ll written\n"; } else { llvm::errs() << "FAILED\n"; std::exit(5); @@ -427,7 +428,7 @@ int main(int argc, char *const argv[]) { suffix == "cuf" || suffix == "CUF" || suffix == "f18" || suffix == "F18" || suffix == "ff18") { fortranSources.push_back(arg); - } else if (suffix == "fir" || suffix == "ir" || suffix == "mlir") { + } else if (suffix == "fir" || suffix == "ir" || suffix == "mlir") { firSources.push_back(arg); } else if (suffix == "o" || suffix == "a") { relocatables.push_back(arg); From 079c7389f15f91759c458c8d33afb7a765c235fe Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Fri, 4 Oct 2019 10:55:44 -0700 Subject: [PATCH 005/123] implement recursive record type (round trips) --- include/fir/FIROps.td | 3 +- include/fir/Type.h | 16 ++- lib/fir/Dialect.cpp | 89 +------------ lib/fir/Type.cpp | 243 ++++++++++++++++++++++++++++-------- test/fir/recursive-type.fir | 5 + 5 files changed, 212 insertions(+), 144 deletions(-) create mode 100644 test/fir/recursive-type.fir diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index c56f60d9da5f..2a659c62bf55 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -469,7 +469,7 @@ class fir_IntegralSwitchTerminatorOp dests; llvm::SmallVector, 4> destArgs; while (true) { - M::Attribute ivalue; + M::IntegerAttr ivalue; M::Block *dest; llvm::SmallVector destArg; llvm::SmallVector temp; @@ -590,6 +590,7 @@ def fir_SelectCaseOp : fir_SwitchTerminatorOp<"select_case"> { return M::failure(); attrs.push_back(attr); if (attr.dyn_cast_or_null()) { + // do nothing } else if (attr.dyn_cast_or_null()) { M::OpAsmParser::OperandType oper1; M::OpAsmParser::OperandType oper2; diff --git a/include/fir/Type.h b/include/fir/Type.h index 4f684b848936..197cff086b0d 100644 --- a/include/fir/Type.h +++ b/include/fir/Type.h @@ -20,6 +20,7 @@ #include "llvm/ADT/Optional.h" namespace llvm { +class raw_ostream; class StringRef; template class ArrayRef; @@ -287,22 +288,25 @@ class RecordType : public mlir::Type::TypeBase lenPList = {}, - llvm::ArrayRef typeList = {}); + static RecordType get(mlir::MLIRContext *ctxt, llvm::StringRef name); + RecordType finalize(llvm::ArrayRef lenPList, + llvm::ArrayRef typeList); constexpr static bool kindof(unsigned kind) { return kind == getId(); } constexpr static unsigned getId() { return TypeKind::FIR_DERIVED; } + detail::RecordTypeStorage const *uniqueKey() const; + static mlir::LogicalResult verifyConstructionInvariants(llvm::Optional loc, - mlir::MLIRContext *context, llvm::StringRef name, - llvm::ArrayRef lenPList, - llvm::ArrayRef typeList); + mlir::MLIRContext *context, + llvm::StringRef name); }; mlir::Type parseFirType(FIROpsDialect *dialect, llvm::StringRef rawData, mlir::Location loc); +void printFirType(FIROpsDialect *dialect, mlir::Type ty, llvm::raw_ostream &os); + } // namespace fir namespace llvm { diff --git a/lib/fir/Dialect.cpp b/lib/fir/Dialect.cpp index 67a9987af341..507848d5dfc4 100644 --- a/lib/fir/Dialect.cpp +++ b/lib/fir/Dialect.cpp @@ -66,95 +66,8 @@ M::Type fir::FIROpsDialect::parseType(llvm::StringRef rawData, return parseFirType(const_cast(this), rawData, loc); } -void printBounds(llvm::raw_ostream &os, const SequenceType::Bounds &bounds) { - char ch = '<'; - for (auto &b : bounds) { - if (b.hasValue()) { - os << ch << *b; - } else { - os << ch << '?'; - } - ch = 'x'; - } -} - void fir::FIROpsDialect::printType(M::Type ty, llvm::raw_ostream &os) const { - if (auto type = ty.dyn_cast()) { - os << "ref<"; - type.getEleTy().print(os); - os << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "logical<" << type.getFKind() << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "real<" << type.getFKind() << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "char<" << type.getFKind() << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "tdesc<"; - type.getOfTy().print(os); - os << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "field"; - } else if (auto type = ty.dyn_cast()) { - os << "box<"; - type.getEleTy().print(os); - os << '>'; - } else if (auto type = ty.dyn_cast()) { - auto eleTy = type.getEleTy().cast(); - os << "boxchar<" << eleTy.getFKind() << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "boxproc<"; - type.getEleTy().print(os); - os << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "dims<" << type.getRank() << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "array"; - auto shape = type.getShape(); - if (shape.hasValue()) { - printBounds(os, *shape); - } else { - os << "<*"; - } - os << ':'; - type.getEleTy().print(os); - os << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "heap<"; - type.getEleTy().print(os); - os << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "ptr<"; - type.getEleTy().print(os); - os << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "type<" << type.getName(); - 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 << '}'; - } - os << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "int<" << type.getFKind() << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "complex<" << type.getFKind() << '>'; - } else { - assert(false); - } + return printFirType(const_cast(this), ty, os); } M::Attribute fir::FIROpsDialect::parseAttribute(llvm::StringRef rawText, diff --git a/lib/fir/Type.cpp b/lib/fir/Type.cpp index a8b18d7142cc..a94e9faf972b 100644 --- a/lib/fir/Type.cpp +++ b/lib/fir/Type.cpp @@ -18,9 +18,11 @@ #include "mlir/IR/Dialect.h" #include "mlir/IR/StandardTypes.h" #include "mlir/Parser.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/raw_ostream.h" namespace L = llvm; namespace M = mlir; @@ -381,7 +383,7 @@ class FIRTypeParser { } // `box` `<` type (',' affine-map)? `>` - BoxType parseBox() { + BoxType parseBox() { if (consumeToken(TokenKind::leftang, "expected '<' in type")) { return {}; } @@ -391,8 +393,9 @@ class FIRTypeParser { return {}; } auto token = lexer.lexToken(); + M::AffineMapAttr map; if (token.kind == TokenKind::comma) { - // TODO: parse AffineMapAttr + map = parseAffineMapAttr(); token = lexer.lexToken(); } if (token.kind != TokenKind::rightang) { @@ -401,7 +404,7 @@ class FIRTypeParser { } if (checkAtEnd()) return {}; - return BoxType::get(ofTy); + return BoxType::get(ofTy, map); } // `boxchar` `<` kind `>` @@ -463,6 +466,8 @@ class FIRTypeParser { M::MLIRContext *getContext() const { return context; } + M::AffineMapAttr parseAffineMapAttr(); + private: M::MLIRContext *context; Lexer lexer; @@ -517,6 +522,11 @@ SequenceType::Shape FIRTypeParser::parseShape() { } } +// affine-map ::= `#` ident +M::AffineMapAttr FIRTypeParser::parseAffineMapAttr() { + return {}; // FIXME opParser->parseAttr(); +} + // bounds ::= lo extent stride | `?` // `array` `<` bounds (`,` bounds)* `:` type (',' affine-map)? `>` SequenceType FIRTypeParser::parseSequence() { @@ -530,8 +540,9 @@ SequenceType FIRTypeParser::parseSequence() { return {}; } auto token = lexer.lexToken(); + M::AffineMapAttr map; if (token.kind == TokenKind::comma) { - // TODO: parse AffineMapAttr + map = parseAffineMapAttr(); token = lexer.lexToken(); } if (token.kind != TokenKind::rightang) { @@ -541,7 +552,7 @@ SequenceType FIRTypeParser::parseSequence() { if (checkAtEnd()) { return {}; } - return SequenceType::get(shape, eleTy); + return SequenceType::get(shape, eleTy, map); } // Parses: string `:` type (',' string `:` type)* '}' @@ -607,8 +618,9 @@ RecordType FIRTypeParser::parseDerived() { emitError(loc, "expected a identifier as name of derived type"); return {}; } + RecordType result = RecordType::get(getContext(), name.text); auto token = lexer.lexToken(); - RecordType::TypeList kindList; + RecordType::TypeList lenParamList; RecordType::TypeList typeList; if (token.kind == TokenKind::leftbrace) { goto parse_fields; @@ -616,7 +628,7 @@ RecordType FIRTypeParser::parseDerived() { // degenerate case? goto check_close; } - kindList = parseLenParamList(); + lenParamList = parseLenParamList(); token = lexer.lexToken(); if (token.kind != TokenKind::leftbrace) { // no fields? @@ -633,7 +645,9 @@ RecordType FIRTypeParser::parseDerived() { if (checkAtEnd()) { return {}; } - return RecordType::get(getContext(), name.text, kindList, typeList); + if (lenParamList.empty() && typeList.empty()) + return result; + return result.finalize(lenParamList, typeList); } M::Type FIRTypeParser::parseType() { @@ -690,7 +704,8 @@ bool singleIndirectionLevel(M::Type ty) { } // namespace -namespace fir::detail { +namespace fir { +namespace detail { // Type storage classes @@ -1062,45 +1077,46 @@ struct SequenceTypeStorage : public M::TypeStorage { /// Derived type storage struct RecordTypeStorage : public M::TypeStorage { - using KeyTy = std::tuple, - L::ArrayRef>; + using KeyTy = L::StringRef; static unsigned hashKey(const KeyTy &key) { - return L::hash_combine(std::get<0>(key).str()); + return L::hash_combine(key.str()); } - bool operator==(const KeyTy &key) const { - return std::get<0>(key) == getName(); - } + bool operator==(const KeyTy &key) const { return key == getName(); } static RecordTypeStorage *construct(M::TypeStorageAllocator &allocator, const KeyTy &key) { auto *storage = allocator.allocate(); - auto &name = std::get<0>(key); - auto &lens = std::get<1>(key); - auto &members = std::get<2>(key); - return new (storage) RecordTypeStorage{name, lens, members}; + return new (storage) RecordTypeStorage{key}; } L::StringRef getName() const { return name; } - // The KindList must be provided at construction for correct hash-consing + void setLenParamList(L::ArrayRef list) { lens = list; } L::ArrayRef getLenParamList() const { return lens; } void setTypeList(L::ArrayRef list) { types = list; } L::ArrayRef getTypeList() const { return types; } + void finalize(L::ArrayRef lenParamList, + L::ArrayRef typeList) { + assert(!finalized && "record type already finalized"); + finalized = true; + setLenParamList(lenParamList); + setTypeList(typeList); + } + protected: std::string name; + bool finalized; std::vector lens; std::vector types; private: RecordTypeStorage() = delete; - explicit RecordTypeStorage(L::StringRef name, - L::ArrayRef lens, - L::ArrayRef types) - : name{name}, lens{lens}, types{types} {} + explicit RecordTypeStorage(L::StringRef name) + : name{name}, finalized{false} {} }; /// Type descriptor type storage @@ -1129,7 +1145,8 @@ struct TypeDescTypeStorage : public M::TypeStorage { explicit TypeDescTypeStorage(M::Type ofTy) : ofTy{ofTy} {} }; -} // namespace fir::detail +} // namespace detail +} // namespace fir template bool inbounds(A v, B lb, B ub) { @@ -1392,20 +1409,8 @@ L::hash_code fir::hash_value(const SequenceType::Shape &sh) { /// /// This type captures a Fortran "derived type" -RecordType fir::RecordType::get(M::MLIRContext *ctxt, L::StringRef name, - L::ArrayRef lenPList, - L::ArrayRef typeList) { - return Base::get(ctxt, FIR_DERIVED, name, lenPList, typeList); -} - -L::StringRef fir::RecordType::getName() { return getImpl()->getName(); } - -RecordType::TypeList fir::RecordType::getTypeList() { - return getImpl()->getTypeList(); -} - -RecordType::TypeList fir::RecordType::getLenParamList() { - return getImpl()->getLenParamList(); +RecordType fir::RecordType::get(M::MLIRContext *ctxt, L::StringRef name) { + return Base::get(ctxt, FIR_DERIVED, name); } inline static bool verifyIntegerType(M::Type ty) { @@ -1419,25 +1424,43 @@ inline static bool verifyRecordMemberType(M::Type ty) { ty.dyn_cast()); } -M::LogicalResult fir::RecordType::verifyConstructionInvariants( - L::Optional, M::MLIRContext *context, L::StringRef name, - L::ArrayRef lenPList, - L::ArrayRef typeList) { - if (name.size() == 0) - return M::failure(); +RecordType fir::RecordType::finalize(L::ArrayRef lenPList, + L::ArrayRef typeList) { + getImpl()->finalize(lenPList, typeList); llvm::StringSet uniq; for (auto &p : lenPList) if (!uniq.insert(p.first).second) - return M::failure(); + return {}; for (auto &p : typeList) if (!uniq.insert(p.first).second) - return M::failure(); + return {}; for (auto &p : lenPList) if (!verifyIntegerType(p.second)) - return M::failure(); + return {}; for (auto &p : typeList) if (!verifyRecordMemberType(p.second)) - return M::failure(); + return {}; + return *this; +} + +L::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(); +} + +M::LogicalResult fir::RecordType::verifyConstructionInvariants( + L::Optional, M::MLIRContext *context, L::StringRef name) { + if (name.size() == 0) + return M::failure(); return M::success(); } @@ -1469,3 +1492,125 @@ M::Type fir::parseFirType(FIROpsDialect *dialect, L::StringRef rawData, FIRTypeParser parser{dialect, rawData, loc}; return parser.parseType(); } + +namespace { +class TypePrinter { +public: + void print(FIROpsDialect *dialect, M::Type ty, llvm::raw_ostream &os) { + if (auto type = ty.dyn_cast()) { + os << "ref<"; + type.getEleTy().print(os); + os << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "logical<" << type.getFKind() << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "real<" << type.getFKind() << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "char<" << type.getFKind() << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "tdesc<"; + type.getOfTy().print(os); + os << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "field"; + } else if (auto type = ty.dyn_cast()) { + os << "box<"; + type.getEleTy().print(os); + if (auto map = type.getLayoutMap()) { + os << ", "; + map.print(os); + } + os << '>'; + } else if (auto type = ty.dyn_cast()) { + auto eleTy = type.getEleTy().cast(); + os << "boxchar<" << eleTy.getFKind() << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "boxproc<"; + type.getEleTy().print(os); + os << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "dims<" << type.getRank() << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "array"; + auto shape = type.getShape(); + if (shape.hasValue()) { + printBounds(os, *shape); + } else { + os << "<*"; + } + os << ':'; + type.getEleTy().print(os); + if (auto map = type.getLayoutMap()) { + os << ", "; + map.print(os); + } + os << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "heap<"; + type.getEleTy().print(os); + os << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "ptr<"; + type.getEleTy().print(os); + os << '>'; + } else if (auto type = ty.dyn_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 << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "int<" << type.getFKind() << '>'; + } else if (auto type = ty.dyn_cast()) { + os << "complex<" << type.getFKind() << '>'; + } else { + assert(false); + } + } + +private: + void printBounds(llvm::raw_ostream &os, const SequenceType::Bounds &bounds) { + char ch = '<'; + for (auto &b : bounds) { + if (b.hasValue()) { + os << ch << *b; + } else { + os << ch << '?'; + } + ch = 'x'; + } + } + + // must be in a global context because the printer must be able to track + // context through multiple recursive invocations of the mlir type printer + static llvm::SmallPtrSet + recordTypeVisited; +}; + +llvm::SmallPtrSet + TypePrinter::recordTypeVisited; // instantiate +} // namespace + +void fir::printFirType(FIROpsDialect *dialect, M::Type ty, + llvm::raw_ostream &os) { + TypePrinter().print(dialect, ty, os); +} diff --git a/test/fir/recursive-type.fir b/test/fir/recursive-type.fir new file mode 100644 index 000000000000..26f76c3d4ddf --- /dev/null +++ b/test/fir/recursive-type.fir @@ -0,0 +1,5 @@ +!t = type !fir.type>}> + +func @a(%a : !t) { + return +} From 2e273178ff057bddb52ff0dbc5bc9796ee60f70f Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Thu, 3 Oct 2019 15:18:46 -0700 Subject: [PATCH 006/123] Add a small framework to lower intrinsic calls to runtime calls. The goal is to abstract which library is targeted while lowering the calls. What matters in this commit is the exposed interface and the dynamic data structure that holds the runtime description. The static description of the runtime used to feed this API is a dummy description. The long term goal is to share this knowledge with the folding code that uses runtime. SPOT. --- lib/burnside/CMakeLists.txt | 1 + lib/burnside/bridge.cc | 10 ++-- lib/burnside/convert-expr.cc | 56 ++++++++++++++++---- lib/burnside/convert-expr.h | 6 ++- lib/burnside/intrinsics.cc | 99 ++++++++++++++++++++++++++++++++++++ lib/burnside/intrinsics.h | 71 ++++++++++++++++++++++++++ 6 files changed, 227 insertions(+), 16 deletions(-) create mode 100644 lib/burnside/intrinsics.cc create mode 100644 lib/burnside/intrinsics.h diff --git a/lib/burnside/CMakeLists.txt b/lib/burnside/CMakeLists.txt index 019fcadb8866..5a29de117ecb 100644 --- a/lib/burnside/CMakeLists.txt +++ b/lib/burnside/CMakeLists.txt @@ -20,6 +20,7 @@ add_library(FortranBurnside convert-expr.cc fe-helper.cc flattened.cc + intrinsics.cc runtime.cc ) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index ebee0dd5224e..28232cf5bcb2 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -20,6 +20,7 @@ #include "fir/FIROps.h" #include "fir/Type.h" #include "flattened.h" +#include "intrinsics.h" #include "runtime.h" #include "../parser/parse-tree-visitor.h" #include "../semantics/tools.h" @@ -73,6 +74,7 @@ class FIRConverter { std::list edgeQ; std::map doMap; SymMap symbolMap; + IntrinsicLibrary intrinsics; Pa::CharBlock lastKnownPos; bool noInsPt{false}; @@ -110,10 +112,10 @@ class FIRConverter { } M::Value *createFIRAddr(M::Location loc, const SomeExpr *expr) { - return createSomeAddress(loc, build(), *expr, symbolMap); + return createSomeAddress(loc, build(), *expr, symbolMap, intrinsics); } M::Value *createFIRExpr(M::Location loc, const SomeExpr *expr) { - return createSomeExpression(loc, build(), *expr, symbolMap); + return createSomeExpression(loc, build(), *expr, symbolMap, intrinsics); } M::Value *createTemp(M::Type type, Se::Symbol *symbol = nullptr) { return createTemporary(toLocation(), build(), symbolMap, type, symbol); @@ -622,7 +624,9 @@ class FIRConverter { public: FIRConverter(BurnsideBridge &bridge) : mlirContext{bridge.getMLIRContext()}, cooked{bridge.getCookedSource()}, - module{bridge.getModule()} {} + module{bridge.getModule()}, intrinsics{IntrinsicLibrary::create( + IntrinsicLibrary::Version::LLVM, + mlirContext)} {} FIRConverter() = delete; template constexpr bool Pre(const A &) { return true; } diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index e670d7603757..24bc2471dc0a 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -18,7 +18,9 @@ #include "fir/Dialect.h" #include "fir/FIROps.h" #include "fir/Type.h" +#include "intrinsics.h" #include "runtime.h" +#include "../common/unwrap.h" #include "../evaluate/fold.h" #include "../evaluate/real.h" #include "../semantics/expression.h" @@ -66,6 +68,7 @@ class ExprLowering { M::OpBuilder &builder; const SomeExpr &expr; SymMap &symMap; + const IntrinsicLibrary &intrinsics; M::Location getLoc() { return location; } @@ -143,8 +146,7 @@ class ExprLowering { return createBinaryOp(ex, genval(ex.left()), genval(ex.right())); } - M::FuncOp getFunction(RuntimeEntryCode callee, M::FunctionType funTy) { - auto name{getRuntimeEntryName(callee)}; + M::FuncOp getFunction(const std::string &name, M::FunctionType funTy) { auto module{getModule(&builder)}; if (M::FuncOp func{getNamedFunction(name)}) { return func; @@ -152,6 +154,11 @@ class ExprLowering { return createFunction(module, name, funTy); } + M::FuncOp getRTFunction(RuntimeEntryCode callee, M::FunctionType funTy) { + auto name{getRuntimeEntryName(callee)}; + return getFunction(name, funTy); + } + // FIXME binary operation :: ('a, 'a) -> 'a template M::FunctionType createFunctionType() { if constexpr (TC == IntegerCat) { @@ -179,7 +186,7 @@ class ExprLowering { operands.push_back(genval(ex.left())); operands.push_back(genval(ex.right())); M::FunctionType funTy = createFunctionType(); - auto func{getFunction(callee, funTy)}; + auto func{getRTFunction(callee, funTy)}; auto x{builder.create(getLoc(), func, operands)}; return x.getResult(0); // FIXME } @@ -551,7 +558,32 @@ class ExprLowering { return gen(funRef); } template M::Value *genval(const Ev::FunctionRef &funRef) { - TODO(); + TODO(); // Derived type functions (user + intrinsics) + } + template + M::Value *genval(const Ev::FunctionRef> &funRef) { + if (const auto &intrinsic{funRef.proc().GetSpecificIntrinsic()}) { + std::string name{intrinsic->name}; + M::Type ty{genTypeFromCategoryAndKind(builder.getContext(), TC, KIND)}; + // Probe the intrinsic library + if (std::optional func{ + intrinsics.getFunction(name, ty, builder)}) { + L::SmallVector operands; + for (const auto &arg : funRef.arguments()) { + if (auto *expr{Ev::UnwrapExpr>(arg)}) { + operands.push_back(genval(*expr)); + } else { + TODO(); // Weird arguments, can it happen at that stage ? + } + } + auto x{builder.create(getLoc(), *func, operands)}; + return x.getResult(0); // FIXME + } else { + TODO(); // No runtime function for this intrinsic. + } + } else { + TODO(); // User defined function. + } } template M::Value *gen(const Ev::Expr &exp) { @@ -571,9 +603,9 @@ class ExprLowering { } public: - explicit ExprLowering( - M::Location loc, M::OpBuilder &bldr, const SomeExpr &vop, SymMap &map) - : location{loc}, builder{bldr}, expr{vop}, symMap{map} {} + explicit ExprLowering(M::Location loc, M::OpBuilder &bldr, + const SomeExpr &vop, SymMap &map, const IntrinsicLibrary &intr) + : location{loc}, builder{bldr}, expr{vop}, symMap{map}, intrinsics{intr} {} /// Lower the expression `expr` into MLIR standard dialect M::Value *gen() { return gen(expr); } @@ -583,14 +615,16 @@ class ExprLowering { } // namespace M::Value *Br::createSomeExpression(M::Location loc, M::OpBuilder &builder, - const Ev::Expr &expr, SymMap &symMap) { - ExprLowering lower{loc, builder, expr, symMap}; + const Ev::Expr &expr, SymMap &symMap, + const IntrinsicLibrary &intrinsics) { + ExprLowering lower{loc, builder, expr, symMap, intrinsics}; return lower.genval(); } M::Value *Br::createSomeAddress(M::Location loc, M::OpBuilder &builder, - const Ev::Expr &expr, SymMap &symMap) { - ExprLowering lower{loc, builder, expr, symMap}; + const Ev::Expr &expr, SymMap &symMap, + const IntrinsicLibrary &intrinsics) { + ExprLowering lower{loc, builder, expr, symMap, intrinsics}; return lower.gen(); } diff --git a/lib/burnside/convert-expr.h b/lib/burnside/convert-expr.h index a07ae02a5b08..1a0bd0859ee6 100644 --- a/lib/burnside/convert-expr.h +++ b/lib/burnside/convert-expr.h @@ -15,6 +15,8 @@ #ifndef FORTRAN_BURNSIDE_CONVERT_EXPR_H_ #define FORTRAN_BURNSIDE_CONVERT_EXPR_H_ +#include "intrinsics.h" + /// [Coding style](https://llvm.org/docs/CodingStandards.html) namespace mlir { @@ -43,10 +45,10 @@ class SymMap; mlir::Value *createSomeExpression(mlir::Location loc, mlir::OpBuilder &builder, const Fortran::evaluate::Expr &expr, - SymMap &symMap); + SymMap &symMap, const IntrinsicLibrary &); mlir::Value *createSomeAddress(mlir::Location loc, mlir::OpBuilder &builder, const Fortran::evaluate::Expr &expr, - SymMap &symMap); + SymMap &symMap, const IntrinsicLibrary &); mlir::Value *createTemporary(mlir::Location loc, mlir::OpBuilder &builder, SymMap &symMap, mlir::Type type, const semantics::Symbol *symbol); diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc new file mode 100644 index 000000000000..bf643b311b1e --- /dev/null +++ b/lib/burnside/intrinsics.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "intrinsics.h" +#include "builder.h" + +/// [Coding style](https://llvm.org/docs/CodingStandards.html) + +namespace Fortran::burnside { + +// Define a simple static runtime description that will be transformed into +// IntrinsicImplementation when building the IntrinsicLibrary. +namespace runtime { +enum class Type { f32, f64 }; +struct StaticDescription { + const char *name; + const char *symbol; + Type resultType; + std::vector argumentTypes; +}; + +// TODO +// This table should be generated in a clever ways and probably shared with +// lib/evaluate intrinsic folding. +static const StaticDescription llvm[] = { + {"abs", "llvm.fabs.f32", Type::f32, {Type::f32}}, + {"abs", "llvm.fabs.f64", Type::f64, {Type::f64}}, + {"acos", "acosf", Type::f32, {Type::f32}}, + {"acos", "acos", Type::f64, {Type::f64}}, + {"atan", "atan2f", Type::f32, {Type::f32, Type::f32}}, + {"atan", "atan2", Type::f64, {Type::f64, Type::f64}}, + {"sqrt", "llvm.sqrt.f32", Type::f32, {Type::f32}}, + {"sqrt", "llvm.sqrt.f64", Type::f64, {Type::f64}}, + {"cos", "llvm.cos.f32", Type::f32, {Type::f32}}, + {"cos", "llvm.cos.f64", Type::f64, {Type::f64}}, + {"sin", "llvm.sin.f32", Type::f32, {Type::f32}}, + {"sin", "llvm.sin.f64", Type::f64, {Type::f64}}, +}; + +// Conversion between types of the static representation and MLIR types. +mlir::Type toMLIRType(Type t, mlir::MLIRContext &context) { + switch (t) { + case Type::f32: return mlir::FloatType::getF32(&context); + case Type::f64: return mlir::FloatType::getF64(&context); + } +} +mlir::FunctionType toMLIRFunctionType( + const StaticDescription &func, mlir::MLIRContext &context) { + std::vector argMLIRTypes; + for (runtime::Type t : func.argumentTypes) { + argMLIRTypes.push_back(toMLIRType(t, context)); + } + return mlir::FunctionType::get( + argMLIRTypes, toMLIRType(func.resultType, context), &context); +} +} // runtime + +std::optional IntrinsicLibrary::getFunction( + const std::string &name, const mlir::Type &type, + mlir::OpBuilder &builder) const { + auto module{getModule(&builder)}; + if (const auto &it{lib.find({name, type})}; it != lib.end()) { + const IntrinsicImplementation &impl{it->second}; + if (mlir::FuncOp func{getNamedFunction(impl.symbol)}) { + return func; + } + mlir::FuncOp function{createFunction(module, impl.symbol, impl.type)}; + function.setAttr("fir.intrinsic", builder.getUnitAttr()); + return function; + } else { + return std::nullopt; + } +} + +// So far ignore the version an only load the dummy llvm lib. +IntrinsicLibrary IntrinsicLibrary::create( + IntrinsicLibrary::Version, mlir::MLIRContext &context) { + Map map; + for (const auto &func : runtime::llvm) { + IntrinsicLibrary::Key key{ + func.name, runtime::toMLIRType(func.resultType, context)}; + IntrinsicImplementation impl{ + func.symbol, runtime::toMLIRFunctionType(func, context)}; + map.insert({key, impl}); + } + return IntrinsicLibrary{std::move(map)}; +} +} diff --git a/lib/burnside/intrinsics.h b/lib/burnside/intrinsics.h new file mode 100644 index 000000000000..05aaede413e5 --- /dev/null +++ b/lib/burnside/intrinsics.h @@ -0,0 +1,71 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_INTRINSICS_H_ +#define FORTRAN_BURNSIDE_INTRINSICS_H_ + +#include "mlir/Dialect/StandardOps/Ops.h" +#include +#include +#include + +/// [Coding style](https://llvm.org/docs/CodingStandards.html) + +namespace Fortran::burnside { + +/// IntrinsicLibrary holds the runtime description of intrinsics. It aims +/// at abstracting which library version is used to implement Fortran +/// numerical intrinsics while lowering expressions. +/// It can be probed for a certain intrinsic and will return an mlir::FuncOp +/// that matches the targeted library implementation. +class IntrinsicLibrary { +public: + /// Available intrinsic library versions. + enum class Version { PgmathFast, PgmathRelaxed, PgmathPrecise, LLVM }; + using Key = std::pair; + // Need a custom hash for this kind of keys. LLVM provides it. + struct Hash { + size_t operator()(const Key &k) const { return llvm::hash_value(k); } + }; + /// Internal structure to describe the runtime function. An intrinsic function + /// is not declared in mlir until the IntrinsicLibrary needs to return it. + /// This is to avoid polluting the LLVM IR with useless declarations. + /// This structure allows generating mlir::FuncOp on the fly. + struct IntrinsicImplementation { + IntrinsicImplementation(const std::string &n, mlir::FunctionType t) + : symbol{n}, type{t} {}; + std::string symbol; + mlir::FunctionType type; + }; + using Map = std::unordered_map; + + /// Probe the intrinsic library for a certain intrinsic and get/build the + /// related mlir::FuncOp if a runtime description is found. + /// Also add an unit attribute "fir.intrinsic" to the function so that later + /// it is possible to quickly know what function are intrinsics vs users. + std::optional getFunction( + const std::string &, const mlir::Type &, mlir::OpBuilder &) const; + + /// Create the runtime description for the targeted library version. + static IntrinsicLibrary create(Version, mlir::MLIRContext &); + +private: + IntrinsicLibrary(Map &&l) : lib{std::move(l)} {}; + /// Holds the intrinsic runtime description to be probed. + Map lib; +}; + +} + +#endif // FORTRAN_BURNSIDE_INTRINSICS_H_ From bebd14979dd06d60f56121a848fc6ddf2ba0b80d Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Fri, 4 Oct 2019 13:52:20 -0700 Subject: [PATCH 007/123] Answer previous PR comments Initial PR: https://github.com/schweitzpgi/f18/pull/2 * std::string -> llvm::StringRef * removed const& around mlir::Type * Added comment about optional argument non handling --- lib/burnside/convert-expr.cc | 14 +++++++++----- lib/burnside/intrinsics.cc | 3 +-- lib/burnside/intrinsics.h | 9 +++++---- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 24bc2471dc0a..fe82ff14399f 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -146,7 +146,7 @@ class ExprLowering { return createBinaryOp(ex, genval(ex.left()), genval(ex.right())); } - M::FuncOp getFunction(const std::string &name, M::FunctionType funTy) { + M::FuncOp getFunction(L::StringRef name, M::FunctionType funTy) { auto module{getModule(&builder)}; if (M::FuncOp func{getNamedFunction(name)}) { return func; @@ -154,7 +154,7 @@ class ExprLowering { return createFunction(module, name, funTy); } - M::FuncOp getRTFunction(RuntimeEntryCode callee, M::FunctionType funTy) { + M::FuncOp getRuntimeFunction(RuntimeEntryCode callee, M::FunctionType funTy) { auto name{getRuntimeEntryName(callee)}; return getFunction(name, funTy); } @@ -186,7 +186,7 @@ class ExprLowering { operands.push_back(genval(ex.left())); operands.push_back(genval(ex.right())); M::FunctionType funTy = createFunctionType(); - auto func{getRTFunction(callee, funTy)}; + auto func{getRuntimeFunction(callee, funTy)}; auto x{builder.create(getLoc(), func, operands)}; return x.getResult(0); // FIXME } @@ -563,7 +563,7 @@ class ExprLowering { template M::Value *genval(const Ev::FunctionRef> &funRef) { if (const auto &intrinsic{funRef.proc().GetSpecificIntrinsic()}) { - std::string name{intrinsic->name}; + L::StringRef name{intrinsic->name}; M::Type ty{genTypeFromCategoryAndKind(builder.getContext(), TC, KIND)}; // Probe the intrinsic library if (std::optional func{ @@ -573,7 +573,11 @@ class ExprLowering { if (auto *expr{Ev::UnwrapExpr>(arg)}) { operands.push_back(genval(*expr)); } else { - TODO(); // Weird arguments, can it happen at that stage ? + // Some intrinsics have optional arguments (inquiry, + // transformational). These kind of intrinsics cannot be blindly + // mapped 1 to 1 to a runtime signature and will require a more + // ad-hoc handling. + TODO(); } } auto x{builder.create(getLoc(), *func, operands)}; diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index bf643b311b1e..f4468e646853 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -67,8 +67,7 @@ mlir::FunctionType toMLIRFunctionType( } // runtime std::optional IntrinsicLibrary::getFunction( - const std::string &name, const mlir::Type &type, - mlir::OpBuilder &builder) const { + llvm::StringRef name, mlir::Type type, mlir::OpBuilder &builder) const { auto module{getModule(&builder)}; if (const auto &it{lib.find({name, type})}; it != lib.end()) { const IntrinsicImplementation &impl{it->second}; diff --git a/lib/burnside/intrinsics.h b/lib/burnside/intrinsics.h index 05aaede413e5..fec48b579450 100644 --- a/lib/burnside/intrinsics.h +++ b/lib/burnside/intrinsics.h @@ -15,6 +15,7 @@ #ifndef FORTRAN_BURNSIDE_INTRINSICS_H_ #define FORTRAN_BURNSIDE_INTRINSICS_H_ +#include "llvm/ADT/StringRef.h" #include "mlir/Dialect/StandardOps/Ops.h" #include #include @@ -33,7 +34,7 @@ class IntrinsicLibrary { public: /// Available intrinsic library versions. enum class Version { PgmathFast, PgmathRelaxed, PgmathPrecise, LLVM }; - using Key = std::pair; + using Key = std::pair; // Need a custom hash for this kind of keys. LLVM provides it. struct Hash { size_t operator()(const Key &k) const { return llvm::hash_value(k); } @@ -43,9 +44,9 @@ class IntrinsicLibrary { /// This is to avoid polluting the LLVM IR with useless declarations. /// This structure allows generating mlir::FuncOp on the fly. struct IntrinsicImplementation { - IntrinsicImplementation(const std::string &n, mlir::FunctionType t) + IntrinsicImplementation(llvm::StringRef n, mlir::FunctionType t) : symbol{n}, type{t} {}; - std::string symbol; + llvm::StringRef symbol; mlir::FunctionType type; }; using Map = std::unordered_map; @@ -55,7 +56,7 @@ class IntrinsicLibrary { /// Also add an unit attribute "fir.intrinsic" to the function so that later /// it is possible to quickly know what function are intrinsics vs users. std::optional getFunction( - const std::string &, const mlir::Type &, mlir::OpBuilder &) const; + llvm::StringRef, mlir::Type, mlir::OpBuilder &) const; /// Create the runtime description for the targeted library version. static IntrinsicLibrary create(Version, mlir::MLIRContext &); From 6206e2a54d0fb05ce804fabdd8cf8ac690ca4c63 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Fri, 4 Oct 2019 15:19:11 -0700 Subject: [PATCH 008/123] refine type checking, add some sketches of tests, etc. convert MemToReg to template form add converter stubs for all FIR ops move the bridge from FIR to standard to be a transformation pass --- include/fir/FIROps.td | 9 +- .../{Tilikum => Transforms}/StdConverter.h | 0 include/fir/Type.h | 4 +- lib/fir/LLVMConverter.cpp | 439 ++++++++++++++++-- lib/fir/MemToReg.cpp | 221 ++++----- lib/fir/StdConverter.cpp | 2 +- lib/fir/Type.cpp | 95 ++-- test/fir/character.fir | 12 + test/fir/embox.fir | 6 + test/fir/fir-dt.fir | 5 + test/fir/fir-ops.fir | 2 +- tools/fml/fml.cc | 2 +- 12 files changed, 611 insertions(+), 186 deletions(-) rename include/fir/{Tilikum => Transforms}/StdConverter.h (100%) create mode 100644 test/fir/character.fir create mode 100644 test/fir/embox.fir create mode 100644 test/fir/fir-dt.fir diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index 2a659c62bf55..97fc157071c5 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -835,7 +835,7 @@ def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]>, Results<(outs fir_BoxType)>, def fir_EmboxCharOp : fir_Op<"emboxchar", [NoSideEffect]>, Results<(outs fir_BoxCharType)>, - Arguments<(ins fir_CharacterType:$memref, AnyIntegerLike:$len)> { + Arguments<(ins AnyReferenceLike:$memref, AnyIntegerLike:$len)> { let summary = "boxes a given CHARACTER reference and its LEN parameter"; let description = [{ @@ -866,6 +866,13 @@ def fir_EmboxCharOp : fir_Op<"emboxchar", [NoSideEffect]>, p << " : "; p.printFunctionalType(getOperation()); }]; + + let verifier = [{ + auto eleTy = elementTypeOf(memref()->getType()); + if (!eleTy.dyn_cast()) + return M::failure(); + return M::success(); + }]; } def fir_EmboxProcOp : fir_Op<"emboxproc", [NoSideEffect]>, diff --git a/include/fir/Tilikum/StdConverter.h b/include/fir/Transforms/StdConverter.h similarity index 100% rename from include/fir/Tilikum/StdConverter.h rename to include/fir/Transforms/StdConverter.h diff --git a/include/fir/Type.h b/include/fir/Type.h index 197cff086b0d..5e4836abf34f 100644 --- a/include/fir/Type.h +++ b/include/fir/Type.h @@ -289,8 +289,8 @@ class RecordType : public mlir::Type::TypeBase lenPList, - llvm::ArrayRef typeList); + void finalize(llvm::ArrayRef lenPList, + llvm::ArrayRef typeList); constexpr static bool kindof(unsigned kind) { return kind == getId(); } constexpr static unsigned getId() { return TypeKind::FIR_DERIVED; } diff --git a/lib/fir/LLVMConverter.cpp b/lib/fir/LLVMConverter.cpp index f57a19bab3f4..73fe316a2f42 100644 --- a/lib/fir/LLVMConverter.cpp +++ b/lib/fir/LLVMConverter.cpp @@ -228,6 +228,19 @@ class FIROpConversion : public M::ConversionPattern { FIRToLLVMTypeConverter &lowering; }; +struct AddrOfOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto addr = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + // convert to LLVM IR dialect `alloca` struct AllocaOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -236,8 +249,9 @@ struct AllocaOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto alloc = M::cast(op); - rewriter.replaceOpWithNewOp( - op, lowering.convertType(alloc.getType()), operands, alloc.getAttrs()); + auto ty = lowering.convertType(alloc.getType()); + rewriter.replaceOpWithNewOp(alloc, ty, operands, + alloc.getAttrs()); return matchSuccess(); } }; @@ -250,9 +264,153 @@ struct AllocMemOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto heap = M::cast(op); + auto ty = lowering.convertType(heap.getType()); // FIXME: should be a call to malloc - rewriter.replaceOpWithNewOp( - op, lowering.convertType(heap.getType()), operands, heap.getAttrs()); + rewriter.replaceOpWithNewOp(heap, ty, operands, + heap.getAttrs()); + return matchSuccess(); + } +}; + +struct BoxAddrOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto boxaddr = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +struct BoxCharLenOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto boxchar = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +struct BoxDimsOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto boxdims = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +struct BoxEleSizeOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto boxelesz = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +struct BoxIsAllocOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto boxisalloc = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +struct BoxIsArrayOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto boxisarray = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +struct BoxIsPtrOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto boxisptr = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +struct BoxProcHostOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto boxprochost = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +struct BoxRankOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto boxrank = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +struct BoxTypeDescOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto boxtypedesc = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +struct CallOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto call = M::cast(op); + // TODO + assert(false); return matchSuccess(); } }; @@ -350,7 +508,63 @@ struct CoordinateOpConversion : public FIROpConversion { } }; -// convert a reference to an LLVM struct value +// virtual call to a method in a dispatch table +struct DispatchOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto dispatch = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +// dispatch table for a Fortran derived type +struct DispatchTableOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto disptable = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +// entry in a dispatch table; binds a method-name to a function +struct DTEntryOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto dtentry = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +// create a CHARACTER box +struct EmboxCharOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto emboxchar = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +// create a generic box on a memory reference struct EmboxOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -390,6 +604,21 @@ struct EmboxOpConversion : public FIROpConversion { } }; +// create a procedure pointer box +struct EmboxProcOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto emboxproc = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +// extract a subobject value from an ssa-value of aggregate type struct ExtractValueOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -403,7 +632,21 @@ struct ExtractValueOpConversion : public FIROpConversion { } }; -struct FreeMemOpConversion : public FIROpConversion { +struct FieldIndexOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto fieldindex = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +// call free function +struct FreeMemOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; M::LLVM::LLVMType getVoidPtrType() const { @@ -425,7 +668,7 @@ struct FreeMemOpConversion : public FIROpConversion { M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { - auto freemem = M::cast(op); + auto freemem = M::cast(op); M::FuncOp freeFunc = genFreeFunc(op, rewriter); M::Value *casted = rewriter.create( op->getLoc(), getVoidPtrType(), operands[0]); @@ -482,19 +725,59 @@ struct GenDimsOpConversion : public FIROpConversion { } }; -#if 0 -class GlobalExprConversion : public FIROpConversion { +struct GenTypeDescOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto gentypedesc = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +struct GlobalEntryOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto globalentry = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +class GlobalOpConversion : public FIROpConversion { public: - explicit GlobalExprConversion( - M::MLIRContext *ctxt, FIRToLLVMTypeConverter &lowering) - : FIROpConversion(GlobalOp::getOperationName(), 1, ctxt, lowering) {} + using FIROpConversion::FIROpConversion; - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto global = M::cast(op); // FIXME + assert(false); + return matchSuccess(); + } +}; + +// indirect call (via a pointer); see dispatch as well +struct ICallOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto icall = M::cast(op); + // TODO + assert(false); + return matchSuccess(); } }; -#endif struct InsertValueOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -509,8 +792,22 @@ struct InsertValueOpConversion : public FIROpConversion { } }; +struct LenParamIndexOpConversion + : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto lenparam = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + // convert to LLVM IR dialect `load` -struct LoadExprConversion : public FIROpConversion { +struct LoadOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; M::PatternMatchResult @@ -527,20 +824,34 @@ struct LoadExprConversion : public FIROpConversion { } }; +// abstract loop construct +struct LoopOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto loop = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + struct NoReassocOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { + auto noreassoc = M::cast(op); // FIXME assert(false); return matchSuccess(); } }; -// conversion of fir::SelectOp -struct SelectOpConversion : public FIROpConversion { +struct SelectCaseOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; M::PatternMatchResult @@ -548,13 +859,15 @@ struct SelectOpConversion : public FIROpConversion { L::ArrayRef destinations, L::ArrayRef destOperands, M::ConversionPatternRewriter &rewriter) const override { + auto selectcase = M::cast(op); // FIXME assert(false); return matchSuccess(); } }; -struct SelectCaseOpConversion : public FIROpConversion { +// conversion of fir::SelectOp +struct SelectOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; M::PatternMatchResult @@ -562,6 +875,7 @@ struct SelectCaseOpConversion : public FIROpConversion { L::ArrayRef destinations, L::ArrayRef destOperands, M::ConversionPatternRewriter &rewriter) const override { + auto select = M::cast(op); // FIXME assert(false); return matchSuccess(); @@ -576,6 +890,7 @@ struct SelectRankOpConversion : public FIROpConversion { L::ArrayRef destinations, L::ArrayRef destOperands, M::ConversionPatternRewriter &rewriter) const override { + auto selectrank = M::cast(op); // FIXME assert(false); return matchSuccess(); @@ -590,6 +905,7 @@ struct SelectTypeOpConversion : public FIROpConversion { L::ArrayRef destinations, L::ArrayRef destOperands, M::ConversionPatternRewriter &rewriter) const override { + auto selecttype = M::cast(op); // FIXME assert(false); return matchSuccess(); @@ -597,13 +913,57 @@ struct SelectTypeOpConversion : public FIROpConversion { }; // convert to LLVM IR dialect `store` -struct StoreExprConversion : public FIROpConversion { +struct StoreOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto store = M::cast(op); + rewriter.replaceOpWithNewOp(store, operands[0], + operands[1]); + return matchSuccess(); + } +}; + +// unbox a CHARACTER box value, yielding its components +struct UnboxCharOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto unboxchar = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +// unbox a generic box value, yielding its components +struct UnboxOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { - rewriter.replaceOpWithNewOp(op, operands[0], operands[1]); + auto unbox = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + +// unbox a procedure box value, yielding its components +struct UnboxProcOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto unboxproc = M::cast(op); + // TODO + assert(false); return matchSuccess(); } }; @@ -638,6 +998,20 @@ struct UnreachableOpConversion : public FIROpConversion { } }; +// abstract conditional construct +struct WhereOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto where = M::cast(op); + // TODO + assert(false); + return matchSuccess(); + } +}; + // Lower a SELECT operation into a cascade of conditional branches. The last // case must be the `true` condition. inline void rewriteSelectConstruct(M::Operation *op, OperandTy operands, @@ -677,12 +1051,23 @@ class FIRToLLVMLoweringPass : public M::ModulePass { auto &context{getContext()}; FIRToLLVMTypeConverter typeConverter{&context}; M::OwningRewritePatternList patterns; - patterns - .insert(&context, typeConverter); + patterns.insert< + AddrOfOpConversion, AllocaOpConversion, AllocMemOpConversion, + BoxAddrOpConversion, BoxCharLenOpConversion, BoxDimsOpConversion, + BoxEleSizeOpConversion, BoxIsAllocOpConversion, BoxIsArrayOpConversion, + BoxIsPtrOpConversion, BoxProcHostOpConversion, BoxRankOpConversion, + BoxTypeDescOpConversion, CallOpConversion, ConvertOpConversion, + CoordinateOpConversion, DispatchOpConversion, DispatchTableOpConversion, + DTEntryOpConversion, EmboxCharOpConversion, EmboxOpConversion, + EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion, + FreeMemOpConversion, GenDimsOpConversion, GenTypeDescOpConversion, + GlobalEntryOpConversion, GlobalOpConversion, ICallOpConversion, + InsertValueOpConversion, LenParamIndexOpConversion, LoadOpConversion, + LoopOpConversion, NoReassocOpConversion, SelectCaseOpConversion, + SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion, + StoreOpConversion, UnboxCharOpConversion, UnboxOpConversion, + UnboxProcOpConversion, UndefOpConversion, UnreachableOpConversion, + WhereOpConversion>(&context, typeConverter); M::populateStdToLLVMConversionPatterns(typeConverter, patterns); M::populateFuncOpTypeConversionPattern(patterns, &context, typeConverter); M::ConversionTarget target{context}; diff --git a/lib/fir/MemToReg.cpp b/lib/fir/MemToReg.cpp index 1f00777d8d7c..76aafbbe8715 100644 --- a/lib/fir/MemToReg.cpp +++ b/lib/fir/MemToReg.cpp @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===- MemToReg.cpp - Generalized mem to reg pass for MLIR dialects ---===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #include "fir/Transforms/MemToReg.h" #include "fir/Analysis/IteratedDominanceFrontier.h" @@ -30,14 +24,19 @@ using namespace fir; using DominatorTree = M::DominanceInfo; +/// A generalized version of a mem-to-reg pass suitable for use with an MLIR +/// dialect. This code was ported from the LLVM project. MLIR differs with its +/// use of block arguments rather than PHI nodes, etc. + namespace { -bool isAllocaPromotable(fir::AllocaOp &ae) { +template +bool isAllocaPromotable(ALLOCA &ae) { for (auto &use : ae.getResult()->getUses()) { auto *op = use.getOwner(); - if (auto load = M::dyn_cast(op)) { + if (auto load = M::dyn_cast(op)) { // do nothing - } else if (auto stor = M::dyn_cast(op)) { + } else if (auto stor = M::dyn_cast(op)) { if (stor.getOperand(0)->getDefiningOp() == op) { return false; } @@ -48,6 +47,7 @@ bool isAllocaPromotable(fir::AllocaOp &ae) { return true; } +template struct AllocaInfo { llvm::SmallVector definingBlocks; llvm::SmallVector usingBlocks; @@ -66,7 +66,7 @@ struct AllocaInfo { /// Scan the uses of the specified alloca, filling in the AllocaInfo used /// by the rest of the pass to reason about the uses of this alloca. - void analyzeAlloca(fir::AllocaOp &AI) { + void analyzeAlloca(ALLOCA &AI) { clear(); // As we scan the uses of the alloca instruction, keep track of stores, @@ -77,12 +77,12 @@ struct AllocaInfo { auto *User = UI->getOwner(); UI++; - if (auto SI = M::dyn_cast(User)) { + if (auto SI = M::dyn_cast(User)) { // Remember the basic blocks which define new values for the alloca definingBlocks.push_back(SI.getOperation()->getBlock()); onlyStore = SI.getOperation(); } else { - auto LI = M::cast(User); + auto LI = M::cast(User); // Otherwise it must be a load instruction, keep track of variable // reads. usingBlocks.push_back(LI.getOperation()->getBlock()); @@ -113,17 +113,18 @@ struct RenamePassData { ValVector Values; }; +template struct LargeBlockInfo { using INMap = llvm::DenseMap; INMap instNumbers; static bool isInterestingInstruction(M::Operation &I) { - if (M::isa(I)) { + if (M::isa(I)) { if (auto *op = I.getOperand(0)->getDefiningOp()) - return M::isa(op); - } else if (M::isa(I)) { + return M::isa(op); + } else if (M::isa(I)) { if (auto *op = I.getOperand(1)->getDefiningOp()) - return M::isa(op); + return M::isa(op); } return false; } @@ -134,17 +135,16 @@ struct LargeBlockInfo { // has it already been numbered? INMap::iterator it = instNumbers.find(oper); - if (it != instNumbers.end()) { + if (it != instNumbers.end()) return it->second; - } // No. search for the oper auto *block = oper->getBlock(); unsigned num = 0u; for (auto &o : block->getOperations()) - if (isInterestingInstruction(o)) { + if (isInterestingInstruction(o)) instNumbers[&o] = num++; - } + it = instNumbers.find(oper); assert(it != instNumbers.end() && "operation not in block?"); return it->second; @@ -159,10 +159,11 @@ struct LargeBlockInfo { void clear() { instNumbers.clear(); } }; -struct MemToReg : public M::FunctionPass { +template +struct MemToReg : public M::FunctionPass> { explicit MemToReg() {} - std::vector allocas; + std::vector allocas; DominatorTree *domTree = nullptr; M::OpBuilder *builder = nullptr; @@ -179,9 +180,10 @@ struct MemToReg : public M::FunctionPass { llvm::DenseMap, unsigned> BlockArgs; llvm::DenseMap, unsigned> argToAllocaMap; - bool rewriteSingleStoreAlloca(fir::AllocaOp &AI, AllocaInfo &Info, - LargeBlockInfo &LBI) { - fir::StoreOp onlyStore(M::cast(Info.onlyStore)); + bool rewriteSingleStoreAlloca(ALLOCA &AI, + AllocaInfo &Info, + LargeBlockInfo &LBI) { + STORE onlyStore(M::cast(Info.onlyStore)); M::Block *StoreBB = Info.onlyStore->getBlock(); int StoreIndex = -1; @@ -193,10 +195,10 @@ struct MemToReg : public M::FunctionPass { auto *UserInst = UI->getOwner(); UI++; - if (M::dyn_cast(UserInst)) { + if (M::dyn_cast(UserInst)) continue; - } - auto LI = M::cast(UserInst); + + auto LI = M::cast(UserInst); // Okay, if we have a load from the alloca, we want to replace it with the // only value stored to the alloca. We can do this if the value is @@ -206,9 +208,8 @@ struct MemToReg : public M::FunctionPass { // If we have a use that is in the same block as the store, compare // the indices of the two instructions to see which one came first. If // the load came before the store, we can't handle it. - if (StoreIndex == -1) { + if (StoreIndex == -1) StoreIndex = LBI.getInstructionIndex(onlyStore); - } if (unsigned(StoreIndex) > LBI.getInstructionIndex(LI)) { // Can't handle this load, bail out. @@ -227,9 +228,8 @@ struct MemToReg : public M::FunctionPass { M::Value *ReplVal = onlyStore.getOperand(0); // If the replacement value is the load, this must occur in unreachable // code. - if (ReplVal == LI.getResult()) { - ReplVal = builder->create(LI.getLoc(), LI.getType()); - } + if (ReplVal == LI.getResult()) + ReplVal = builder->create(LI.getLoc(), LI.getType()); LI.replaceAllUsesWith(ReplVal); LI.erase(); @@ -247,18 +247,17 @@ struct MemToReg : public M::FunctionPass { return true; } - bool promoteSingleBlockAlloca(fir::AllocaOp &AI, AllocaInfo &Info, - LargeBlockInfo &LBI) { + bool promoteSingleBlockAlloca(ALLOCA &AI, + AllocaInfo &Info, + LargeBlockInfo &LBI) { // Walk the use-def list of the alloca, getting the locations of all stores. - using StoresByIndexTy = - llvm::SmallVector, 64>; + using StoresByIndexTy = llvm::SmallVector, 64>; StoresByIndexTy StoresByIndex; for (auto U = AI.getResult()->use_begin(), E = AI.getResult()->use_end(); U != E; U++) - if (fir::StoreOp SI = M::dyn_cast(U->getOwner())) { + if (STORE SI = M::dyn_cast(U->getOwner())) StoresByIndex.emplace_back(LBI.getInstructionIndex(SI), &SI); - } // Sort the stores by their index, making it efficient to do a lookup with a // binary search. @@ -268,23 +267,21 @@ struct MemToReg : public M::FunctionPass { // store above them, if any. for (auto UI = AI.getResult()->use_begin(), E = AI.getResult()->use_end(); UI != E;) { - auto LI = M::dyn_cast(UI->getOwner()); + auto LI = M::dyn_cast(UI->getOwner()); UI++; - if (!LI) { + if (!LI) continue; - } unsigned LoadIdx = LBI.getInstructionIndex(LI); // Find the nearest store that has a lower index than this load. typename StoresByIndexTy::iterator I = llvm::lower_bound( - StoresByIndex, - std::make_pair(LoadIdx, static_cast(nullptr)), + StoresByIndex, std::make_pair(LoadIdx, static_cast(nullptr)), llvm::less_first()); if (I == StoresByIndex.begin()) { if (StoresByIndex.empty()) { // If there are no stores, the load takes the undef value. - auto undef = builder->create(LI.getLoc(), LI.getType()); + auto undef = builder->create(LI.getLoc(), LI.getType()); LI.replaceAllUsesWith(undef.getResult()); } else { // There is no store before this load, bail out (load may be affected @@ -299,9 +296,8 @@ struct MemToReg : public M::FunctionPass { // If the replacement value is the load, this must occur in unreachable // code. - if (ReplVal == LI) { - ReplVal = builder->create(LI.getLoc(), LI.getType()); - } + if (ReplVal == LI) + ReplVal = builder->create(LI.getLoc(), LI.getType()); LI.replaceAllUsesWith(ReplVal); } @@ -313,20 +309,18 @@ struct MemToReg : public M::FunctionPass { // Remove the (now dead) stores and alloca. while (!AI.use_empty()) { auto *ae = AI.getResult(); - for (auto ai = ae->use_begin(), E = ae->use_end(); ai != E; ai++) { - if (fir::StoreOp SI = - M::dyn_cast(ai->get()->getDefiningOp())) { + for (auto ai = ae->user_begin(), E = ae->user_end(); ai != E; ai++) + if (STORE SI = M::dyn_cast(*ai)) { SI.erase(); LBI.deleteValue(SI); } - } } AI.erase(); return true; } - void computeLiveInBlocks(fir::AllocaOp &ae, AllocaInfo &Info, + void computeLiveInBlocks(ALLOCA &ae, AllocaInfo &Info, const llvm::SmallPtrSetImpl &DefBlocks, llvm::SmallPtrSetImpl &liveInBlks) { auto *AI = ae.getOperation(); @@ -341,18 +335,16 @@ struct MemToReg : public M::FunctionPass { // use, the value isn't really live-in. for (unsigned i = 0, e = LiveInBlockWorklist.size(); i != e; ++i) { M::Block *BB = LiveInBlockWorklist[i]; - if (!DefBlocks.count(BB)) { + if (!DefBlocks.count(BB)) continue; - } // Okay, this is a block that both uses and defines the value. If the // first reference to the alloca is a def (store), then we know it isn't // live-in. for (M::Block::iterator I = BB->begin();; ++I) { - if (auto SI = M::dyn_cast(I)) { - if (SI.getOperand(1)->getDefiningOp() != AI) { + if (STORE SI = M::dyn_cast(I)) { + if (SI.getOperand(1)->getDefiningOp() != AI) continue; - } // We found a store to the alloca before a load. The alloca is not // actually live-in here. @@ -363,12 +355,11 @@ struct MemToReg : public M::FunctionPass { break; } - if (auto LI = M::dyn_cast(I)) + if (auto LI = M::dyn_cast(I)) // Okay, we found a load before a store to the alloca. It is actually // live into this block. - if (LI.getOperand()->getDefiningOp() == AI) { + if (LI.getOperand()->getDefiningOp() == AI) break; - } } } @@ -379,18 +370,16 @@ struct MemToReg : public M::FunctionPass { // The block really is live in here, insert it into the set. If already // in the set, then it has already been processed. - if (!liveInBlks.insert(BB).second) { + if (!liveInBlks.insert(BB).second) continue; - } // Since the value is live into BB, it is either defined in a predecessor // or live into it to. Add the preds to the worklist unless they are a // defining block. for (M::Block *P : BB->getPredecessors()) { // The value is not live into a predecessor if it defines the value. - if (DefBlocks.count(P)) { + if (DefBlocks.count(P)) continue; - } // Otherwise it is, add to the worklist. LiveInBlockWorklist.push_back(P); @@ -398,14 +387,12 @@ struct MemToReg : public M::FunctionPass { } } - bool addBlockArgument(M::Block *BB, fir::AllocaOp &Alloca, - unsigned allocaNum) { + bool addBlockArgument(M::Block *BB, ALLOCA &Alloca, unsigned allocaNum) { auto *ae = Alloca.getOperation(); auto key = std::make_pair(BB, ae); auto argNoIter = BlockArgs.find(key); - if (argNoIter != BlockArgs.end()) { + if (argNoIter != BlockArgs.end()) return false; - } auto argNo = BB->getNumArguments(); BB->addArgument(Alloca.getAllocatedType()); BlockArgs[key] = argNo; @@ -418,15 +405,15 @@ struct MemToReg : public M::FunctionPass { M::Block *dest, unsigned size, unsigned ai, M::Value *val, A &&oldOpers) { unsigned i = 0; - for (auto v : oldOpers) { + for (auto v : oldOpers) opers[i++] = v; - } + // we must fill additional args with temporary undef values for (; i < size; ++i) { if (i == ai) continue; auto opTy = dest->getArgument(i)->getType(); - auto typedUndef = builder->create(loc, opTy); + auto typedUndef = builder->create(loc, opTy); opers[i] = typedUndef; } opers[ai] = val; @@ -506,9 +493,8 @@ struct MemToReg : public M::FunctionPass { inline static void addValue(RenamePassData::ValVector &vector, RenamePassData::ValVector::size_type size, M::Value *value) { - if (vector.size() < size + 1) { + if (vector.size() < size + 1) vector.resize(size + 1); - } vector[size] = value; } @@ -534,9 +520,8 @@ struct MemToReg : public M::FunctionPass { } // Don't revisit blocks. - if (!Visited.insert(BB).second) { + if (!Visited.insert(BB).second) return; - } M::Block::iterator II = BB->begin(); while (true) { @@ -545,46 +530,40 @@ struct MemToReg : public M::FunctionPass { M::Operation &opn = *II; II++; - if (auto LI = M::dyn_cast(opn)) { + if (auto LI = M::dyn_cast(opn)) { auto *srcOpn = LI.getOperand()->getDefiningOp(); - if (!srcOpn) { + if (!srcOpn) continue; - } - auto Src = M::dyn_cast(srcOpn); - if (!Src) { + auto Src = M::dyn_cast(srcOpn); + if (!Src) continue; - } llvm::DenseMap::iterator AI = allocaLookup.find(srcOpn); - if (AI == allocaLookup.end()) { + if (AI == allocaLookup.end()) continue; - } M::Value *V = IncomingVals[AI->second]; // Anything using the load now uses the current value. LI.replaceAllUsesWith(V); LI.erase(); - } else if (auto SI = M::dyn_cast(opn)) { + } else if (auto SI = M::dyn_cast(opn)) { auto *dstOpn = SI.getOperand(1)->getDefiningOp(); - if (!dstOpn) { + if (!dstOpn) continue; - } // Delete this instruction and mark the name as the current holder of // the value - auto Dest = M::dyn_cast(dstOpn); - if (!Dest) { + auto Dest = M::dyn_cast(dstOpn); + if (!Dest) continue; - } llvm::DenseMap::iterator ai = allocaLookup.find(dstOpn); - if (ai == allocaLookup.end()) { + if (ai == allocaLookup.end()) continue; - } // what value were we writing? unsigned AllocaNo = ai->second; @@ -596,9 +575,8 @@ struct MemToReg : public M::FunctionPass { // 'Recurse' to our successors. auto I = BB->succ_begin(); auto E = BB->succ_end(); - if (I == E) { + if (I == E) return; - } // Keep track of the successors so we don't visit the same successor twice llvm::SmallPtrSet VisitedSuccs; @@ -616,10 +594,10 @@ struct MemToReg : public M::FunctionPass { } void doPromotion() { - auto F = getFunction(); - std::vector aes; - AllocaInfo info; - LargeBlockInfo lbi; + auto F = this->getFunction(); + std::vector aes; + AllocaInfo info; + LargeBlockInfo lbi; ForwardIDFCalculator IDF(*domTree); assert(!allocas.empty()); @@ -627,7 +605,7 @@ struct MemToReg : public M::FunctionPass { for (unsigned allocaNum = 0, End = allocas.size(); allocaNum != End; ++allocaNum) { auto ae = allocas[allocaNum]; - assert(ae.getParentOfType() == F); + assert(ae.template getParentOfType() == F); if (ae.use_empty()) { ae.erase(); continue; @@ -635,13 +613,11 @@ struct MemToReg : public M::FunctionPass { info.analyzeAlloca(ae); builder->setInsertionPointToStart(&F.front()); if (info.definingBlocks.size() == 1) - if (rewriteSingleStoreAlloca(ae, info, lbi)) { + if (rewriteSingleStoreAlloca(ae, info, lbi)) continue; - } if (info.onlyUsedInOneBlock) - if (promoteSingleBlockAlloca(ae, info, lbi)) { + if (promoteSingleBlockAlloca(ae, info, lbi)) continue; - } // If we haven't computed a numbering for the BB's in the function, do // so now. @@ -685,10 +661,10 @@ struct MemToReg : public M::FunctionPass { aes.push_back(ae); } + std::swap(allocas, aes); - if (allocas.empty()) { + if (allocas.empty()) return; - } lbi.clear(); @@ -696,10 +672,9 @@ struct MemToReg : public M::FunctionPass { // of the alloca's. We do this in case there is a load of a value that // has not been stored yet. In this case, it will get this null value. RenamePassData::ValVector Values(allocas.size()); - for (unsigned i = 0, e = allocas.size(); i != e; ++i) { - Values[i] = builder->create(allocas[i].getLoc(), - allocas[i].getAllocatedType()); - } + for (unsigned i = 0, e = allocas.size(); i != e; ++i) + Values[i] = builder->create(allocas[i].getLoc(), + allocas[i].getAllocatedType()); // Walks all basic blocks in the function performing the SSA rename // algorithm and inserting the phi nodes we marked as necessary @@ -723,7 +698,7 @@ struct MemToReg : public M::FunctionPass { // in unreachable basic blocks that were not processed by walking the // dominator tree. Just delete the users now. if (!A->use_empty()) { - auto undef = builder->create(aa.getLoc(), aa.getType()); + auto undef = builder->create(aa.getLoc(), aa.getType()); aa.replaceAllUsesWith(undef.getResult()); } aa.erase(); @@ -732,26 +707,24 @@ struct MemToReg : public M::FunctionPass { /// run the MemToReg pass on the FIR dialect void runOnFunction() override { - auto f = getFunction(); + auto f = this->getFunction(); auto &entry = f.front(); auto bldr = M::OpBuilder(f.getBody()); - domTree = &getAnalysis(); + domTree = &this->template getAnalysis(); builder = &bldr; while (true) { allocas.clear(); for (auto &op : entry) - if (auto ae = M::dyn_cast(op)) { - if (isAllocaPromotable(ae)) { + if (ALLOCA ae = M::dyn_cast(op)) + if (isAllocaPromotable(ae)) allocas.push_back(ae); - } - } - if (allocas.empty()) { + if (allocas.empty()) break; - } + doPromotion(); } @@ -763,5 +736,5 @@ struct MemToReg : public M::FunctionPass { } // namespace std::unique_ptr fir::createMemToRegPass() { - return std::make_unique(); + return std::make_unique>(); } diff --git a/lib/fir/StdConverter.cpp b/lib/fir/StdConverter.cpp index d4caae2457a0..b766630a9126 100644 --- a/lib/fir/StdConverter.cpp +++ b/lib/fir/StdConverter.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "fir/Tilikum/StdConverter.h" +#include "fir/Transforms/StdConverter.h" #include "fir/Dialect.h" #include "fir/FIROps.h" #include "fir/Type.h" diff --git a/lib/fir/Type.cpp b/lib/fir/Type.cpp index a94e9faf972b..70a861ba9961 100644 --- a/lib/fir/Type.cpp +++ b/lib/fir/Type.cpp @@ -453,6 +453,9 @@ class FIRTypeParser { RecordType::TypeList parseTypeList(); RecordType::TypeList parseLenParamList(); RecordType parseDerived(); + RecordType verifyDerived(RecordType derivedTy, + llvm::ArrayRef lenPList, + llvm::ArrayRef typeList); // `tdesc` `<` type `>` TypeDescType parseTypeDesc() { return parseTypeSingleton(); } @@ -605,6 +608,63 @@ RecordType::TypeList FIRTypeParser::parseLenParamList() { } } +bool verifyIntegerType(M::Type ty) { + return ty.dyn_cast() || ty.dyn_cast(); +} + +bool verifyRecordMemberType(M::Type ty) { + return !(ty.dyn_cast() || ty.dyn_cast() || + ty.dyn_cast() || ty.dyn_cast() || + ty.dyn_cast() || ty.dyn_cast() || + ty.dyn_cast()); +} + +bool verifySameLists(L::ArrayRef a1, + L::ArrayRef a2) { + if (a1.size() != a2.size()) + return false; + auto iter = a1.begin(); + for (auto lp : a2) { + if (!((iter->first == lp.first) && (iter->second == lp.second))) + return false; + ++iter; + } + return true; +} + +RecordType +FIRTypeParser::verifyDerived(RecordType derivedTy, + L::ArrayRef lenPList, + L::ArrayRef typeList) { + if (!verifySameLists(derivedTy.getLenParamList(), lenPList) || + !verifySameLists(derivedTy.getTypeList(), typeList)) { + emitError(loc, "cannot redefine record type members"); + return {}; + } + for (auto &p : lenPList) + if (!verifyIntegerType(p.second)) { + emitError(loc, "LEN parameter must be integral type"); + return {}; + } + for (auto &p : typeList) + if (!verifyRecordMemberType(p.second)) { + emitError(loc, "field parameter has invalid type"); + return {}; + } + llvm::StringSet uniq; + for (auto &p : lenPList) + if (!uniq.insert(p.first).second) { + emitError(loc, "LEN parameter cannot have duplicate name"); + return {}; + } + for (auto &p : typeList) + if (!uniq.insert(p.first).second) { + emitError(loc, "field cannot have duplicate name"); + return {}; + } + return derivedTy; +} + // Fortran derived type // `type` `<` name // (`(` id `:` type (`,` id `:` type)* `)`)? @@ -647,7 +707,8 @@ RecordType FIRTypeParser::parseDerived() { } if (lenParamList.empty() && typeList.empty()) return result; - return result.finalize(lenParamList, typeList); + result.finalize(lenParamList, typeList); + return verifyDerived(result, lenParamList, typeList); } M::Type FIRTypeParser::parseType() { @@ -1101,7 +1162,8 @@ struct RecordTypeStorage : public M::TypeStorage { void finalize(L::ArrayRef lenParamList, L::ArrayRef typeList) { - assert(!finalized && "record type already finalized"); + if (finalized) + return; finalized = true; setLenParamList(lenParamList); setTypeList(typeList); @@ -1413,34 +1475,9 @@ RecordType fir::RecordType::get(M::MLIRContext *ctxt, L::StringRef name) { return Base::get(ctxt, FIR_DERIVED, name); } -inline static bool verifyIntegerType(M::Type ty) { - return ty.dyn_cast() || ty.dyn_cast(); -} - -inline static bool verifyRecordMemberType(M::Type ty) { - return !(ty.dyn_cast() || ty.dyn_cast() || - ty.dyn_cast() || ty.dyn_cast() || - ty.dyn_cast() || ty.dyn_cast() || - ty.dyn_cast()); -} - -RecordType fir::RecordType::finalize(L::ArrayRef lenPList, - L::ArrayRef typeList) { +void fir::RecordType::finalize(L::ArrayRef lenPList, + L::ArrayRef typeList) { getImpl()->finalize(lenPList, typeList); - llvm::StringSet uniq; - for (auto &p : lenPList) - if (!uniq.insert(p.first).second) - return {}; - for (auto &p : typeList) - if (!uniq.insert(p.first).second) - return {}; - for (auto &p : lenPList) - if (!verifyIntegerType(p.second)) - return {}; - for (auto &p : typeList) - if (!verifyRecordMemberType(p.second)) - return {}; - return *this; } L::StringRef fir::RecordType::getName() { return getImpl()->getName(); } diff --git a/test/fir/character.fir b/test/fir/character.fir new file mode 100644 index 000000000000..c4a11a607d7a --- /dev/null +++ b/test/fir/character.fir @@ -0,0 +1,12 @@ + +fir.global @name constant : !fir.char<1> { + constant "Your name" + //constant 1 +} + +func @get_name() -> !fir.boxchar<1> { + %j1 = fir.address_of (@name_constant) : !fir.ref> + %j2 = constant 9 : i64 + %j3 = fir.emboxchar %j1, %j2 : (!fir.ref>, i64) -> !fir.boxchar<1> + return %j3 : !fir.boxchar<1> +} diff --git a/test/fir/embox.fir b/test/fir/embox.fir new file mode 100644 index 000000000000..e3750a3c8b55 --- /dev/null +++ b/test/fir/embox.fir @@ -0,0 +1,6 @@ +#x0 = (d0, d1) -> (d1, d0) + +func @f(%arg : !fir.ref>) { + %1 = fir.embox %arg [#x0] : (!fir.ref>) -> !fir.box, > + return +} diff --git a/test/fir/fir-dt.fir b/test/fir/fir-dt.fir new file mode 100644 index 000000000000..7dbd6bd13187 --- /dev/null +++ b/test/fir/fir-dt.fir @@ -0,0 +1,5 @@ +func @method_impl(!fir.box>) + +fir.dispatch_table @dispatch_tbl { + fir.dt_entry method, @method_impl +} diff --git a/test/fir/fir-ops.fir b/test/fir/fir-ops.fir index f114346824d9..9f2b3ece4050 100644 --- a/test/fir/fir-ops.fir +++ b/test/fir/fir-ops.fir @@ -99,7 +99,7 @@ func @boxing_match() { %c8 = constant 8 : i32 %3 = fir.alloca !fir.char<1> : !fir.ref> %b3 = fir.undefined !fir.char<1> - %4 = fir.emboxchar %b3, %c8 : (!fir.char<1>, i32) -> !fir.boxchar<1> + %4 = fir.emboxchar %3, %c8 : (!fir.ref>, i32) -> !fir.boxchar<1> %5:2 = fir.unboxchar %4 : (!fir.boxchar<1>) -> (!fir.ref>, i32) %6 = fir.alloca tuple : !fir.ref> %a1 = fir.undefined tuple diff --git a/tools/fml/fml.cc b/tools/fml/fml.cc index 823dcddb378a..8592704df2c3 100644 --- a/tools/fml/fml.cc +++ b/tools/fml/fml.cc @@ -16,8 +16,8 @@ #include "fir/Dialect.h" #include "fir/Tilikum/LLVMConverter.h" -#include "fir/Tilikum/StdConverter.h" #include "fir/Transforms/MemToReg.h" +#include "fir/Transforms/StdConverter.h" #include "../../lib/burnside/bridge.h" #include "../../lib/burnside/convert-expr.h" #include "../../lib/common/default-kinds.h" From a73d0300f8aec5477f2c8bc6e510dc31f86abe79 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Mon, 7 Oct 2019 17:30:07 -0700 Subject: [PATCH 009/123] Removed mlir::ModuleManager from the bridge mlir::ModuleManager is not required anymore, mlir::ModuleOp can directly be used as a mlir::SymbolTable to querry symbols. The mlir::ModuleManager is usefull to create distinct temp names from a variable name, but it does not sync its symbole table automatically with the module it manages. Keeping a ModuleManager in a high level context seems to be too risky. For instance, there was currently a bug where function names were directly created in the Module but looked-up in the ModuleManager that could not find them. --- lib/burnside/bridge.cc | 9 ++------- lib/burnside/bridge.h | 2 -- lib/burnside/builder.cc | 4 ++-- lib/burnside/builder.h | 2 +- lib/burnside/convert-expr.cc | 2 +- lib/burnside/intrinsics.cc | 2 +- 6 files changed, 7 insertions(+), 14 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 28232cf5bcb2..516148ff5843 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -122,7 +122,7 @@ class FIRConverter { } M::FuncOp genFunctionFIR(llvm::StringRef callee, M::FunctionType funcTy) { - if (auto func{getNamedFunction(callee)}) { + if (auto func{getNamedFunction(getMod(), callee)}) { return func; } return createFunction(getMod(), callee, funcTy); @@ -895,7 +895,7 @@ void FIRConverter::genFIR(AnalysisData &ad, std::list &operations) { template void FIRConverter::translateRoutine( const A &routine, llvm::StringRef name, const Se::Symbol *funcSym) { - M::FuncOp func{getNamedFunction(name)}; + M::FuncOp func{getNamedFunction(getMod(), name)}; if (!func) { // get arguments and return type if any, otherwise just use empty vectors llvm::SmallVector args; @@ -960,10 +960,6 @@ void Br::BurnsideBridge::parseSourceFile(llvm::SourceMgr &srcMgr) { auto owningRef = M::parseSourceFile(srcMgr, context.get()); module.reset(new M::ModuleOp(owningRef.get().getOperation())); owningRef.release(); - if (validModule()) { - // symbols are added by ModuleManager ctor - manager.reset(new M::ModuleManager(getModule())); - } } Br::BurnsideBridge::BurnsideBridge( @@ -973,7 +969,6 @@ Br::BurnsideBridge::BurnsideBridge( context = std::make_unique(); module = std::make_unique( M::ModuleOp::create(M::UnknownLoc::get(context.get()))); - manager = std::make_unique(getModule()); } void Br::instantiateBurnsideBridge( diff --git a/lib/burnside/bridge.h b/lib/burnside/bridge.h index f0d153f4913f..04af2d250351 100644 --- a/lib/burnside/bridge.h +++ b/lib/burnside/bridge.h @@ -52,7 +52,6 @@ class BurnsideBridge { } mlir::MLIRContext &getMLIRContext() { return *context.get(); } - mlir::ModuleManager &getManager() { return *manager.get(); } mlir::ModuleOp &getModule() { return *module.get(); } void parseSourceFile(llvm::SourceMgr &); @@ -75,7 +74,6 @@ class BurnsideBridge { const parser::CookedSource *cooked; std::unique_ptr context; std::unique_ptr module; - std::unique_ptr manager; }; /// Cross the bridge from the Fortran parse-tree, etc. to FIR+OpenMP+MLIR diff --git a/lib/burnside/builder.cc b/lib/burnside/builder.cc index 00aec267309f..c626525f53f4 100644 --- a/lib/burnside/builder.cc +++ b/lib/burnside/builder.cc @@ -38,8 +38,8 @@ mlir::FuncOp B::createFunction( return func; } -mlir::FuncOp B::getNamedFunction(llvm::StringRef name) { - return getBridge().getManager().lookupSymbol(name); +mlir::FuncOp B::getNamedFunction(mlir::ModuleOp module, llvm::StringRef name) { + return module.lookupSymbol(name); } void B::SymMap::addSymbol(const semantics::Symbol *symbol, mlir::Value *value) { diff --git a/lib/burnside/builder.h b/lib/burnside/builder.h index 2e13a0f76153..0d0c9c11086e 100644 --- a/lib/burnside/builder.h +++ b/lib/burnside/builder.h @@ -67,7 +67,7 @@ inline mlir::Block *createBlock(mlir::OpBuilder *bldr) { } /// Get a function by name (or null) -mlir::FuncOp getNamedFunction(llvm::StringRef name); +mlir::FuncOp getNamedFunction(mlir::ModuleOp, llvm::StringRef name); /// Create a new Function mlir::FuncOp createFunction( diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index fe82ff14399f..77c753938b01 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -148,7 +148,7 @@ class ExprLowering { M::FuncOp getFunction(L::StringRef name, M::FunctionType funTy) { auto module{getModule(&builder)}; - if (M::FuncOp func{getNamedFunction(name)}) { + if (M::FuncOp func{getNamedFunction(module, name)}) { return func; } return createFunction(module, name, funTy); diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index f4468e646853..3dc6cc5388e1 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -71,7 +71,7 @@ std::optional IntrinsicLibrary::getFunction( auto module{getModule(&builder)}; if (const auto &it{lib.find({name, type})}; it != lib.end()) { const IntrinsicImplementation &impl{it->second}; - if (mlir::FuncOp func{getNamedFunction(impl.symbol)}) { + if (mlir::FuncOp func{getNamedFunction(module, impl.symbol)}) { return func; } mlir::FuncOp function{createFunction(module, impl.symbol, impl.type)}; From 63b1b0b83a55df31aa8b17f3799ae2561e17ac65 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 8 Oct 2019 15:55:24 -0700 Subject: [PATCH 010/123] work on tilikum bridge, lowering types add complex primitives; start lowering some FIR to LLVM IR --- documentation/FIRLangRef.md | 12 +- include/fir/FIROps.td | 26 + .../Tilikum/{LLVMConverter.h => Tilikum.h} | 9 +- lib/fir/CMakeLists.txt | 2 +- lib/fir/FIROps.cpp | 9 + lib/fir/{LLVMConverter.cpp => Tilikum.cpp} | 457 ++++++++---------- test/fir/complex.fir | 6 + tools/fml/fml.cc | 2 +- 8 files changed, 270 insertions(+), 253 deletions(-) rename include/fir/Tilikum/{LLVMConverter.h => Tilikum.h} (82%) rename lib/fir/{LLVMConverter.cpp => Tilikum.cpp} (76%) create mode 100644 test/fir/complex.fir diff --git a/documentation/FIRLangRef.md b/documentation/FIRLangRef.md index 533bd4353c59..c097df2ef550 100644 --- a/documentation/FIRLangRef.md +++ b/documentation/FIRLangRef.md @@ -858,6 +858,16 @@ Example: %91 = fir.dispatch "methodA"(%89, %90) : (!fir.box>, !fir.ref) -> i32 ``` +### Complex Ops + +The standard dialect does not have primitive operations for complex types. +We've added these primitives in the FIR dialect. + +#### `fir.addc` +#### `fir.subc` +#### `fir.mulc` +#### `fir.divc` + ### Other Ops #### `fir.address_of` @@ -934,7 +944,7 @@ FMA operation. #### `fir.global` Syntax: -
fir.global @global-name [ constant ] {
+
fir.global @global-name [ constant ] : type {
    initializer-list
}
diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index 97fc157071c5..77e403c7f1b2 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -1613,6 +1613,32 @@ def fir_DispatchOp : fir_Op<"dispatch", []>, }]; } +// Complex operations + +class fir_ArithmeticOp traits = []> : + fir_Op { + + let results = (outs AnyType); + + let parser = [{ + return impl::parseOneResultSameOperandTypeOp(parser, result); + }]; + + let printer = [{ + return fir::printComplexBinaryOp(this->getOperation(), p); + }]; +} + +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">; + // Other misc. operations def fir_AddrOfOp : fir_OneResultOp<"address_of", [NoSideEffect]> { diff --git a/include/fir/Tilikum/LLVMConverter.h b/include/fir/Tilikum/Tilikum.h similarity index 82% rename from include/fir/Tilikum/LLVMConverter.h rename to include/fir/Tilikum/Tilikum.h index 06702c770c06..d08737ea80ba 100644 --- a/include/fir/Tilikum/LLVMConverter.h +++ b/include/fir/Tilikum/Tilikum.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIR_LLVM_CONVERTER_H -#define FIR_LLVM_CONVERTER_H +#ifndef FIR_TILIKUM_TILIKUM_H +#define FIR_TILIKUM_TILIKUM_H #include @@ -26,12 +26,9 @@ namespace fir { /// Convert FIR to the LLVM IR dialect std::unique_ptr createFIRToLLVMPass(); -/// Convert the standard dialect to LLVM IR dialect -std::unique_ptr createStdToLLVMPass(); - /// Convert the LLVM IR dialect to LLVM-IR proper std::unique_ptr createLLVMDialectToLLVMPass(); } // namespace fir -#endif // FIR_LLVM_CONVERTER_H +#endif // FIR_TILIKUM_TILIKUM_H diff --git a/lib/fir/CMakeLists.txt b/lib/fir/CMakeLists.txt index dbe3a5e265fc..47ae32e73fe8 100644 --- a/lib/fir/CMakeLists.txt +++ b/lib/fir/CMakeLists.txt @@ -17,9 +17,9 @@ add_library(FIR Dialect.cpp FIROps.cpp IteratedDominanceFrontier.cpp - LLVMConverter.cpp MemToReg.cpp StdConverter.cpp + Tilikum.cpp Type.cpp ) diff --git a/lib/fir/FIROps.cpp b/lib/fir/FIROps.cpp index ab3df559de0a..ead744b4300e 100644 --- a/lib/fir/FIROps.cpp +++ b/lib/fir/FIROps.cpp @@ -347,6 +347,15 @@ M::ParseResult parseSelector(M::OpAsmParser &parser, M::OperationState &result, return M::success(); } +void printComplexBinaryOp(Operation *op, OpAsmPrinter &p) { + assert(op->getNumOperands() == 2 && "binary op should have two operands"); + assert(op->getNumResults() == 1 && "binary op should have one result"); + + p << op->getName() << ' ' << *op->getOperand(0) << ", " << *op->getOperand(1); + p.printOptionalAttrDict(op->getAttrs()); + p << " : " << op->getResult(0)->getType(); +} + // Tablegen operators #define GET_OP_CLASSES diff --git a/lib/fir/LLVMConverter.cpp b/lib/fir/Tilikum.cpp similarity index 76% rename from lib/fir/LLVMConverter.cpp rename to lib/fir/Tilikum.cpp index 73fe316a2f42..204fe03a1e87 100644 --- a/lib/fir/LLVMConverter.cpp +++ b/lib/fir/Tilikum.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "fir/Tilikum/LLVMConverter.h" +#include "fir/Tilikum/Tilikum.h" #include "fir/Dialect.h" #include "fir/FIROps.h" #include "fir/Type.h" @@ -33,8 +33,8 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_ostream.h" -// This module performs the conversion of FIR operations to MLIR standard and/or -// LLVM-IR dialects. +/// The bridge that performs the conversion of FIR and standard dialect +/// operations to the LLVM-IR dialect. namespace L = llvm; namespace M = mlir; @@ -53,163 +53,183 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { public: using LLVMTypeConverter::LLVMTypeConverter; + M::LLVM::LLVMType dimsType() { + auto i64Ty = M::LLVM::LLVMType::getInt64Ty(llvmDialect); + return M::LLVM::LLVMType::getVectorTy(i64Ty, 3); + } + + // FIXME: Currently this is a stub. This should correspond to the descriptor + // as defined ISO_Fortran_binding.h and the addendum defined in descriptor.h + M::LLVM::LLVMType convertBoxType(BoxType box) { + // (buffer*, ele-size, rank, type-descriptor, attribute, [dims]) + L::SmallVector parts; + // buffer* + M::Type ele = box.getEleTy(); + auto *ctx = box.getContext(); + auto eleTy = unwrap(convertType(ele)); + parts.push_back(eleTy.getPointerTo()); + // ele-size + parts.push_back(M::LLVM::LLVMType::getInt64Ty(llvmDialect)); + // rank + parts.push_back(convertTypeDescType(ctx)); + // attribute + parts.push_back(M::LLVM::LLVMType::getInt64Ty(llvmDialect)); + // [(int,int,int)] + parts.push_back(dimsType().getPointerTo()); + return M::LLVM::LLVMType::getStructTy(llvmDialect, parts); + } + + M::LLVM::LLVMType convertBoxCharType(BoxCharType boxchar) { + auto ptrTy = convertCharType(boxchar.getEleTy()).getPointerTo(); + auto i64Ty = M::LLVM::LLVMType::getInt64Ty(llvmDialect); + L::SmallVector tuple = {ptrTy, i64Ty}; + return M::LLVM::LLVMType::getStructTy(llvmDialect, tuple); + } + + // fir.boxproc --> llvm<"{ FT*, i8* }"> + M::LLVM::LLVMType convertBoxProcType(BoxProcType boxproc) { + auto funcTy = convertType(boxproc.getEleTy()); + auto ptrTy = unwrap(funcTy).getPointerTo(); + auto i8Ty = M::LLVM::LLVMType::getInt8Ty(llvmDialect); + L::SmallVector tuple = {ptrTy, i8Ty}; + return M::LLVM::LLVMType::getStructTy(llvmDialect, tuple); + } + + M::LLVM::LLVMType convertCharType(CharacterType charTy) { + return convertIntLike(charTy); + } + + M::LLVM::LLVMType convertComplexType(KindTy kind) { + auto realTy = convertRealType(kind); + L::SmallVector tuple = {realTy, realTy}; + return M::LLVM::LLVMType::getStructTy(llvmDialect, tuple); + } + + template + M::LLVM::LLVMType convertIntLike(A intLike) { + return M::LLVM::LLVMType::getIntNTy(llvmDialect, intLike.getSizeInBits()); + } + + LLVM::LLVMType getDefaultInt() { + // FIXME: this should be tied to the front-end default + return M::LLVM::LLVMType::getInt64Ty(llvmDialect); + } + + template + M::LLVM::LLVMType convertPointerLike(A &ty) { + auto eleTy = unwrap(convertType(ty.getEleTy())); + return eleTy.getPointerTo(); + } + // convert a front-end kind value to either a std or LLVM IR dialect type - static M::Type kindToRealType(M::MLIRContext *ctx, L::LLVMContext &llvmCtx, - KindTy kind) { + M::LLVM::LLVMType convertRealType(KindTy kind) { + auto *mlirContext = llvmDialect->getContext(); switch (kind) { - case 0: - return M::FloatType::getF32(ctx); // FIXME: use defaulted kind case 2: - return M::FloatType::getF16(ctx); + return M::LLVM::LLVMType::getHalfTy(llvmDialect); case 3: - return M::FloatType::getBF16(ctx); + emitError(UnknownLoc::get(mlirContext), + "unsupported type: !fir.real<3>, BF16"); + return {}; case 4: - return M::FloatType::getF32(ctx); + return M::LLVM::LLVMType::getFloatTy(llvmDialect); case 8: - return M::FloatType::getF64(ctx); + return M::LLVM::LLVMType::getDoubleTy(llvmDialect); case 10: - return M::LLVM::LLVMType::get(ctx, L::Type::getX86_FP80Ty(llvmCtx)); + // return M::LLVM::LLVMType::get(ctx, L::Type::getX86_FP80Ty(llvmCtx)); + emitError(UnknownLoc::get(mlirContext), + "unsupported type: !fir.real<10>"); + return {}; case 16: - return M::LLVM::LLVMType::get(ctx, L::Type::getFP128Ty(llvmCtx)); + // return M::LLVM::LLVMType::get(ctx, L::Type::getFP128Ty(llvmCtx)); + emitError(UnknownLoc::get(mlirContext), + "unsupported type: !fir.real<16>"); + return {}; } - assert(!kind && "unhandled kind"); + emitError(UnknownLoc::get(mlirContext)) + << "unsupported type: !fir.real<" << kind << ">"; return {}; } - // lower the type descriptor - M::Type convertTypeDescType(M::MLIRContext *ctx) { - auto &llvmCtx = getLLVMContext(); - auto i64 = L::Type::getIntNTy(llvmCtx, 64); - return M::LLVM::LLVMType::get(ctx, i64->getPointerTo()); + M::LLVM::LLVMType convertRecordType(RecordType derived) { + // FIXME: implement + assert(false); + return {}; } - template - M::Type convertPointerLike(A &ty) { - M::Type ele = ty.getEleTy(); - auto eleTy = convertType(ele); - if (ele.dyn_cast()) { - return eleTy; + M::LLVM::LLVMType convertSequenceType(SequenceType seq) { + // FIXME: we don't want to lower to std.memref type + auto shape = seq.getShape(); + auto eleTy = unwrap(convertType(seq.getEleTy())); + L::SmallVector memshape; + if (shape.hasValue()) { + for (auto bi : *shape) { + if (bi.hasValue()) { + memshape.push_back(*bi); + } else { + memshape.push_back(-1); // unknown shape + } + } } - auto *ptrTy = eleTy.cast().getUnderlyingType(); - return M::LLVM::LLVMType::get(ty.getContext(), ptrTy->getPointerTo()); + std::reverse(memshape.begin(), memshape.end()); + return {}; + } + + // lower the type descriptor + M::LLVM::LLVMType convertTypeDescType(M::MLIRContext *ctx) { + // FIXME: using an i64* for now + auto i64Ty = M::LLVM::LLVMType::getInt64Ty(llvmDialect); + return i64Ty.getPointerTo(); } /// Convert FIR types to LLVM IR dialect types M::Type convertType(M::Type t) override { - auto &llvmCtx = getLLVMContext(); - if (auto box = t.dyn_cast()) { - // (buffer*, ele-size, rank, type-descriptor, attribute, [dims]) - L::SmallVector parts; - // buffer* - M::Type ele = box.getEleTy(); - auto *ctx = box.getContext(); - M::Type eleTy = convertType(ele); - L::Type *i64 = L::Type::getIntNTy(llvmCtx, 64); - if (ele.dyn_cast()) { - parts.push_back(eleTy); - } else { - auto *ptrTy = eleTy.cast().getUnderlyingType(); - parts.push_back(M::LLVM::LLVMType::get(ctx, ptrTy->getPointerTo())); - } - // ele-size - parts.push_back(M::LLVM::LLVMType::get(ctx, i64)); - // rank - parts.push_back(convertTypeDescType(ctx)); - // attribute - parts.push_back(M::LLVM::LLVMType::get(ctx, i64)); - // [(int,int,int)] - parts.push_back(M::LLVM::LLVMType::get( - ctx, L::ArrayType::get(i64, 3)->getPointerTo())); - // ... - return LLVMTypeConverter::convertType(M::TupleType::get(parts, ctx)); - } - if (auto boxchar = t.dyn_cast()) { - // (buffer*, buffer-size) - L::SmallVector parts; - // buffer* - parts.push_back(M::LLVM::LLVMType::get(boxchar.getContext(), - L::Type::getIntNTy(llvmCtx, 64))); - // ... - assert(false); - return t; // fixme - } - if (auto boxproc = t.dyn_cast()) { - // (function*, host-context*) - assert(false); - return t; // fixme - } - if (auto chr = t.dyn_cast()) { - L::Type *llTy = L::Type::getIntNTy(llvmCtx, chr.getSizeInBits()); - return M::LLVM::LLVMType::get(chr.getContext(), llTy); - } - if (auto cplx = t.dyn_cast()) { - M::Type realTy = - kindToRealType(cplx.getContext(), llvmCtx, cplx.getFKind()); - return LLVMTypeConverter::convertType(M::ComplexType::get(realTy)); - } - if (auto derived = t.dyn_cast()) { - assert(false); - return t; // fixme - } - if (auto dims = t.dyn_cast()) { - // [rank x ] - L::Type *i64 = L::Type::getIntNTy(llvmCtx, 64); - if (auto rank = dims.getRank()) { - return M::LLVM::LLVMType::get( - dims.getContext(), - L::ArrayType::get(L::VectorType::get(i64, 3), rank)); - } - return M::LLVM::LLVMType::get(dims.getContext(), - L::VectorType::get(i64, 3)->getPointerTo()); - } - if (auto field = t.dyn_cast()) { - return M::LLVM::LLVMType::get(field.getContext(), - L::Type::getIntNTy(llvmCtx, 64)); - } - if (auto heap = t.dyn_cast()) { + if (auto box = t.dyn_cast()) + return convertBoxType(box); + if (auto boxchar = t.dyn_cast()) + return convertBoxCharType(boxchar); + if (auto boxproc = t.dyn_cast()) + return convertBoxProcType(boxproc); + if (auto charTy = t.dyn_cast()) + return convertCharType(charTy); + if (auto cplx = t.dyn_cast()) + return convertComplexType(cplx.getFKind()); + if (auto derived = t.dyn_cast()) + return convertRecordType(derived); + if (auto dims = t.dyn_cast()) + return M::LLVM::LLVMType::getArrayTy(dimsType(), dims.getRank()); + if (auto field = t.dyn_cast()) + return M::LLVM::LLVMType::getInt64Ty(llvmDialect); + if (auto heap = t.dyn_cast()) return convertPointerLike(heap); - } - if (auto integer = t.dyn_cast()) { - L::Type *llTy = L::Type::getIntNTy(llvmCtx, integer.getSizeInBits()); - return M::LLVM::LLVMType::get(integer.getContext(), llTy); - } - if (auto log = t.dyn_cast()) { - L::Type *llTy = L::Type::getIntNTy(llvmCtx, log.getSizeInBits()); - return M::LLVM::LLVMType::get(log.getContext(), llTy); - } - if (auto pointer = t.dyn_cast()) { + if (auto integer = t.dyn_cast()) + return convertIntLike(integer); + if (auto log = t.dyn_cast()) + return convertIntLike(log); + if (auto pointer = t.dyn_cast()) return convertPointerLike(pointer); - } - if (auto real = t.dyn_cast()) { - M::Type realTy = - kindToRealType(real.getContext(), llvmCtx, real.getFKind()); - return LLVMTypeConverter::convertType(realTy); - } - if (auto ref = t.dyn_cast()) { + if (auto real = t.dyn_cast()) + return convertRealType(real.getFKind()); + if (auto ref = t.dyn_cast()) return convertPointerLike(ref); - } - if (auto seq = t.dyn_cast()) { - M::Type eleTy = convertType(seq.getEleTy()); - auto shape = seq.getShape(); - L::SmallVector memshape; - if (shape.hasValue()) { - for (auto bi : *shape) { - if (bi.hasValue()) { - memshape.push_back(*bi); - } else { - memshape.push_back(-1); // unknown shape - } - } - } - std::reverse(memshape.begin(), memshape.end()); - return LLVMTypeConverter::convertType( - M::MemRefType::get(memshape, eleTy)); - } - if (auto tdesc = t.dyn_cast()) { + if (auto sequence = t.dyn_cast()) + return convertSequenceType(sequence); + if (auto tdesc = t.dyn_cast()) return convertTypeDescType(tdesc.getContext()); - } return LLVMTypeConverter::convertType(t); } + + // cloned from LLVMTypeConverter since this is private there + LLVM::LLVMType unwrap(Type type) { + if (!type) + return nullptr; + auto *mlirContext = type.getContext(); + auto wrappedLLVMType = type.dyn_cast(); + if (!wrappedLLVMType) + emitError(UnknownLoc::get(mlirContext), + "conversion resulted in a non-LLVM type"); + return wrappedLLVMType; + } }; /// FIR conversion pattern template @@ -235,8 +255,9 @@ struct AddrOfOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto addr = M::cast(op); - // TODO - assert(false); + auto ty = lowering.unwrap(lowering.convertType(addr.getType())); + rewriter.replaceOpWithNewOp(addr, ty, addr.symbol(), + addr.getAttrs()); return matchSuccess(); } }; @@ -280,7 +301,7 @@ struct BoxAddrOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto boxaddr = M::cast(op); // TODO - assert(false); + assert(false && boxaddr); return matchSuccess(); } }; @@ -293,7 +314,7 @@ struct BoxCharLenOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto boxchar = M::cast(op); // TODO - assert(false); + assert(false && boxchar); return matchSuccess(); } }; @@ -306,7 +327,7 @@ struct BoxDimsOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto boxdims = M::cast(op); // TODO - assert(false); + assert(false && boxdims); return matchSuccess(); } }; @@ -319,7 +340,7 @@ struct BoxEleSizeOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto boxelesz = M::cast(op); // TODO - assert(false); + assert(false && boxelesz); return matchSuccess(); } }; @@ -332,7 +353,7 @@ struct BoxIsAllocOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto boxisalloc = M::cast(op); // TODO - assert(false); + assert(false && boxisalloc); return matchSuccess(); } }; @@ -345,7 +366,7 @@ struct BoxIsArrayOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto boxisarray = M::cast(op); // TODO - assert(false); + assert(false && boxisarray); return matchSuccess(); } }; @@ -358,7 +379,7 @@ struct BoxIsPtrOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto boxisptr = M::cast(op); // TODO - assert(false); + assert(false && boxisptr); return matchSuccess(); } }; @@ -371,7 +392,7 @@ struct BoxProcHostOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto boxprochost = M::cast(op); // TODO - assert(false); + assert(false && boxprochost); return matchSuccess(); } }; @@ -384,7 +405,7 @@ struct BoxRankOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto boxrank = M::cast(op); // TODO - assert(false); + assert(false && boxrank); return matchSuccess(); } }; @@ -397,7 +418,7 @@ struct BoxTypeDescOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto boxtypedesc = M::cast(op); // TODO - assert(false); + assert(false && boxtypedesc); return matchSuccess(); } }; @@ -410,7 +431,7 @@ struct CallOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto call = M::cast(op); // TODO - assert(false); + assert(false && call); return matchSuccess(); } }; @@ -503,7 +524,7 @@ struct CoordinateOpConversion : public FIROpConversion { rewriter.replaceOp(op, v); return matchSuccess(); } - assert(false); // FIXME + assert(false && coor); // FIXME return matchSuccess(); } }; @@ -517,7 +538,7 @@ struct DispatchOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto dispatch = M::cast(op); // TODO - assert(false); + assert(false && dispatch); return matchSuccess(); } }; @@ -531,7 +552,7 @@ struct DispatchTableOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto disptable = M::cast(op); // TODO - assert(false); + assert(false && disptable); return matchSuccess(); } }; @@ -545,7 +566,7 @@ struct DTEntryOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto dtentry = M::cast(op); // TODO - assert(false); + assert(false && dtentry); return matchSuccess(); } }; @@ -559,7 +580,7 @@ struct EmboxCharOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto emboxchar = M::cast(op); // TODO - assert(false); + assert(false && emboxchar); return matchSuccess(); } }; @@ -568,38 +589,12 @@ struct EmboxCharOpConversion : public FIROpConversion { struct EmboxOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::Value *insertValue(M::Location loc, int index, M::Value *aggr, - M::Value *op, M::Type partTy, - M::ConversionPatternRewriter &rewriter) const { - L::SmallVector attrs; - attrs.push_back(rewriter.getI64IntegerAttr(index)); - return rewriter.create( - loc, partTy, aggr, op, rewriter.getArrayAttr(attrs)); - } - M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto embox = M::cast(op); - auto *ctx = embox.getContext(); - M::Type convTy = - lowering.convertType(embox.getType().cast().getEleTy()); - L::Type *eleTy = - convTy.cast().getUnderlyingType()->getPointerTo(); - L::SmallVector members; - members.push_back(eleTy); - M::Type opTy = lowering.convertType(operands[1]->getType()); - L::Type *dimLLVMType = opTy.cast().getUnderlyingType(); - members.push_back(dimLLVMType); - L::Type *boxLLVMType = L::StructType::get(getLLVMContext(), members); - M::Type boxTy = M::LLVM::LLVMType::get(ctx, boxLLVMType); - auto loc = embox.getLoc(); - M::Value *u = rewriter.create(loc, boxTy, - L::ArrayRef{}); - auto *v = insertValue(loc, 0, u, operands[0], boxTy, rewriter); - M::Type dimTy = M::LLVM::LLVMType::get(ctx, dimLLVMType); - auto *w = insertValue(loc, 1, v, operands[1], dimTy, rewriter); - rewriter.replaceOp(op, w); + // TODO + assert(false && embox); return matchSuccess(); } }; @@ -613,7 +608,7 @@ struct EmboxProcOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto emboxproc = M::cast(op); // TODO - assert(false); + assert(false && emboxproc); return matchSuccess(); } }; @@ -627,7 +622,7 @@ struct ExtractValueOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto extractVal = M::cast(op); // FIXME - assert(false); + assert(false && extractVal); return matchSuccess(); } }; @@ -640,7 +635,7 @@ struct FieldIndexOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto fieldindex = M::cast(op); // TODO - assert(false); + assert(false && fieldindex); return matchSuccess(); } }; @@ -669,11 +664,11 @@ struct FreeMemOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto freemem = M::cast(op); - M::FuncOp freeFunc = genFreeFunc(op, rewriter); + M::FuncOp freeFunc = genFreeFunc(freemem, rewriter); M::Value *casted = rewriter.create( - op->getLoc(), getVoidPtrType(), operands[0]); + freemem.getLoc(), getVoidPtrType(), operands[0]); rewriter.replaceOpWithNewOp( - op, llvm::ArrayRef(), rewriter.getSymbolRefAttr(freeFunc), + freemem, llvm::ArrayRef(), rewriter.getSymbolRefAttr(freeFunc), casted); return matchSuccess(); } @@ -686,41 +681,9 @@ struct GenDimsOpConversion : public FIROpConversion { M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { - auto argsize = operands.size(); - assert(argsize % 3 == 0 && "must be a multiple of 3"); - auto loc = op->getLoc(); - auto *ctx = op->getContext(); - auto &llvmCtx = getLLVMContext(); - L::Type *i64 = L::Type::getIntNTy(llvmCtx, 64); - L::Type *v3i64 = L::VectorType::get(i64, 3); - M::Type vec3 = M::LLVM::LLVMType::get(ctx, v3i64); - L::Type *av3i64 = L::ArrayType::get(v3i64, argsize / 3); - M::Type arrvec3 = M::LLVM::LLVMType::get(ctx, av3i64); - auto i32Type = M::LLVM::LLVMType::get(ctx, L::Type::getIntNTy(llvmCtx, 32)); - M::Value *v = rewriter.create(loc, arrvec3, - L::ArrayRef{}); - // BUG? insertelement requires an i32 for 3rd argument - M::Value *zero = rewriter.create( - loc, i32Type, rewriter.getI64IntegerAttr(0)); - M::Value *one = rewriter.create( - loc, i32Type, rewriter.getI64IntegerAttr(1)); - M::Value *two = rewriter.create( - loc, i32Type, rewriter.getI64IntegerAttr(2)); - unsigned rank = 0; - for (std::size_t i = 0; i < argsize;) { - auto a1 = rewriter.create(loc, vec3); - auto a2 = rewriter.create(loc, vec3, a1, - operands[i++], zero); - auto a3 = rewriter.create(loc, vec3, a2, - operands[i++], one); - auto a4 = rewriter.create(loc, vec3, a3, - operands[i++], two); - L::SmallVector attrs; - attrs.push_back(rewriter.getI64IntegerAttr(rank++)); - v = rewriter.create(loc, arrvec3, v, a4, - rewriter.getArrayAttr(attrs)); - } - rewriter.replaceOp(op, v); + auto gendims = M::cast(op); + // TODO + assert(false && gendims); return matchSuccess(); } }; @@ -733,7 +696,7 @@ struct GenTypeDescOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto gentypedesc = M::cast(op); // TODO - assert(false); + assert(false && gentypedesc); return matchSuccess(); } }; @@ -746,7 +709,7 @@ struct GlobalEntryOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto globalentry = M::cast(op); // TODO - assert(false); + assert(false && globalentry); return matchSuccess(); } }; @@ -759,8 +722,19 @@ class GlobalOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto global = M::cast(op); - // FIXME - assert(false); + auto tyAttr = M::TypeAttr::get(lowering.convertType(global.getType())); + M::UnitAttr isConst; + if (global.getAttrOfType("constant").getValue()) + isConst = M::UnitAttr::get(global.getContext()); + auto name = M::StringAttr::get( + global.getAttrOfType(M::SymbolTable::getSymbolAttrName()) + .getValue(), + global.getContext()); + M::Attribute value; + auto addrSpace = /* FIXME: hard-coded i32 here; is that ok? */ + M::IntegerAttr::get(M::IntegerType::get(32, global.getContext()), 0); + rewriter.replaceOpWithNewOp(global, tyAttr, isConst, + name, value, addrSpace); return matchSuccess(); } }; @@ -774,7 +748,7 @@ struct ICallOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto icall = M::cast(op); // TODO - assert(false); + assert(false && icall); return matchSuccess(); } }; @@ -787,7 +761,7 @@ struct InsertValueOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto insertVal = cast(op); // FIXME - assert(false); + assert(false && insertVal); return matchSuccess(); } }; @@ -801,7 +775,7 @@ struct LenParamIndexOpConversion M::ConversionPatternRewriter &rewriter) const override { auto lenparam = M::cast(op); // TODO - assert(false); + assert(false && lenparam); return matchSuccess(); } }; @@ -833,7 +807,7 @@ struct LoopOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto loop = M::cast(op); // TODO - assert(false); + assert(false && loop); return matchSuccess(); } }; @@ -846,7 +820,7 @@ struct NoReassocOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto noreassoc = M::cast(op); // FIXME - assert(false); + assert(false && noreassoc); return matchSuccess(); } }; @@ -861,7 +835,7 @@ struct SelectCaseOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto selectcase = M::cast(op); // FIXME - assert(false); + assert(false && selectcase); return matchSuccess(); } }; @@ -877,7 +851,7 @@ struct SelectOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto select = M::cast(op); // FIXME - assert(false); + assert(false && select); return matchSuccess(); } }; @@ -892,7 +866,7 @@ struct SelectRankOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto selectrank = M::cast(op); // FIXME - assert(false); + assert(false && selectrank); return matchSuccess(); } }; @@ -907,7 +881,7 @@ struct SelectTypeOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto selecttype = M::cast(op); // FIXME - assert(false); + assert(false && selecttype); return matchSuccess(); } }; @@ -935,7 +909,7 @@ struct UnboxCharOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto unboxchar = M::cast(op); // TODO - assert(false); + assert(false && unboxchar); return matchSuccess(); } }; @@ -949,7 +923,7 @@ struct UnboxOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto unbox = M::cast(op); // TODO - assert(false); + assert(false && unbox); return matchSuccess(); } }; @@ -963,7 +937,7 @@ struct UnboxProcOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto unboxproc = M::cast(op); // TODO - assert(false); + assert(false && unboxproc); return matchSuccess(); } }; @@ -1007,7 +981,7 @@ struct WhereOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto where = M::cast(op); // TODO - assert(false); + assert(false && where); return matchSuccess(); } }; @@ -1112,11 +1086,6 @@ std::unique_ptr fir::createFIRToLLVMPass() { return std::make_unique(); } -// returns the predefined pass -std::unique_ptr fir::createStdToLLVMPass() { - return M::createLowerToLLVMPass(); -} - std::unique_ptr fir::createLLVMDialectToLLVMPass() { return std::make_unique(); } diff --git a/test/fir/complex.fir b/test/fir/complex.fir new file mode 100644 index 000000000000..4ccaf74e76e4 --- /dev/null +++ b/test/fir/complex.fir @@ -0,0 +1,6 @@ +func @add(%a : !fir.complex<4>, %b : !fir.complex<4>) -> !fir.complex<4> + +func @foo(%a : !fir.complex<4>, %b : !fir.complex<4>) -> !fir.complex<4> { + %1 = call @add(%a, %b) : (!fir.complex<4>, !fir.complex<4>) -> !fir.complex<4> + return %1 : !fir.complex<4> +} diff --git a/tools/fml/fml.cc b/tools/fml/fml.cc index 8592704df2c3..fd8019480793 100644 --- a/tools/fml/fml.cc +++ b/tools/fml/fml.cc @@ -15,7 +15,7 @@ // Temporary Fortran front end driver main program for development scaffolding. #include "fir/Dialect.h" -#include "fir/Tilikum/LLVMConverter.h" +#include "fir/Tilikum/Tilikum.h" #include "fir/Transforms/MemToReg.h" #include "fir/Transforms/StdConverter.h" #include "../../lib/burnside/bridge.h" From 28b400b3ba958bb1d69694a78e63ec43ebf86180 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Wed, 9 Oct 2019 14:04:51 -0700 Subject: [PATCH 011/123] add code to allow cleanup on attribute dicts add FirEndOp converter --- lib/fir/Tilikum.cpp | 56 ++++++++++++++++++++++++++++++++----------- test/fir/addrof.1.fir | 9 +++++++ test/fir/complex.fir | 11 +++++---- test/fir/complex.mlir | 6 +++++ 4 files changed, 63 insertions(+), 19 deletions(-) create mode 100644 test/fir/addrof.1.fir create mode 100644 test/fir/complex.mlir diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index 204fe03a1e87..475cfcfba4e5 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -232,6 +232,23 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { } }; +L::SmallVector +pruneNamedAttrDict(L::ArrayRef attrs, + L::ArrayRef omitNames) { + L::SmallVector result; + for (auto x : attrs) { + bool omit = false; + for (auto o : omitNames) + if (x.first.strref() == o) { + omit = true; + break; + } + if (!omit) + result.push_back(x); + } + return result; +} + /// FIR conversion pattern template template class FIROpConversion : public M::ConversionPattern { @@ -256,8 +273,9 @@ struct AddrOfOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto addr = M::cast(op); auto ty = lowering.unwrap(lowering.convertType(addr.getType())); + auto attrs = pruneNamedAttrDict(addr.getAttrs(), {"symbol"}); rewriter.replaceOpWithNewOp(addr, ty, addr.symbol(), - addr.getAttrs()); + attrs); return matchSuccess(); } }; @@ -300,8 +318,7 @@ struct BoxAddrOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxaddr = M::cast(op); - // TODO - assert(false && boxaddr); + rewriter.replaceOpWithNewOp(boxaddr, M::Type{}, operands); return matchSuccess(); } }; @@ -600,7 +617,7 @@ struct EmboxOpConversion : public FIROpConversion { }; // create a procedure pointer box -struct EmboxProcOpConversion : public FIROpConversion { +struct EmboxProcOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; M::PatternMatchResult @@ -614,7 +631,7 @@ struct EmboxProcOpConversion : public FIROpConversion { }; // extract a subobject value from an ssa-value of aggregate type -struct ExtractValueOpConversion : public FIROpConversion { +struct ExtractValueOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; M::PatternMatchResult @@ -640,6 +657,17 @@ struct FieldIndexOpConversion : public FIROpConversion { } }; +struct FirEndOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + rewriter.replaceOp(op, {}); + return matchSuccess(); + } +}; + // call free function struct FreeMemOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -1033,15 +1061,15 @@ class FIRToLLVMLoweringPass : public M::ModulePass { BoxTypeDescOpConversion, CallOpConversion, ConvertOpConversion, CoordinateOpConversion, DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion, EmboxCharOpConversion, EmboxOpConversion, - EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion, - FreeMemOpConversion, GenDimsOpConversion, GenTypeDescOpConversion, - GlobalEntryOpConversion, GlobalOpConversion, ICallOpConversion, - InsertValueOpConversion, LenParamIndexOpConversion, LoadOpConversion, - LoopOpConversion, NoReassocOpConversion, SelectCaseOpConversion, - SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion, - StoreOpConversion, UnboxCharOpConversion, UnboxOpConversion, - UnboxProcOpConversion, UndefOpConversion, UnreachableOpConversion, - WhereOpConversion>(&context, typeConverter); + EmboxProcOpConversion, FirEndOpConversion, ExtractValueOpConversion, + FieldIndexOpConversion, FreeMemOpConversion, GenDimsOpConversion, + GenTypeDescOpConversion, GlobalEntryOpConversion, GlobalOpConversion, + ICallOpConversion, InsertValueOpConversion, LenParamIndexOpConversion, + LoadOpConversion, LoopOpConversion, NoReassocOpConversion, + SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion, + SelectTypeOpConversion, StoreOpConversion, UnboxCharOpConversion, + UnboxOpConversion, UnboxProcOpConversion, UndefOpConversion, + UnreachableOpConversion, WhereOpConversion>(&context, typeConverter); M::populateStdToLLVMConversionPatterns(typeConverter, patterns); M::populateFuncOpTypeConversionPattern(patterns, &context, typeConverter); M::ConversionTarget target{context}; diff --git a/test/fir/addrof.1.fir b/test/fir/addrof.1.fir new file mode 100644 index 000000000000..f9fd66dd6ed5 --- /dev/null +++ b/test/fir/addrof.1.fir @@ -0,0 +1,9 @@ +// Tilikum tests - fir.end is not legalized + +fir.global @var_x : !fir.int<4> {} + +func @getAddressOfX() -> !fir.ref> { + %1 = fir.address_of(@var_x) : !fir.ref> + return %1 : !fir.ref> +} + diff --git a/test/fir/complex.fir b/test/fir/complex.fir index 4ccaf74e76e4..9f69e41bbd2b 100644 --- a/test/fir/complex.fir +++ b/test/fir/complex.fir @@ -1,6 +1,7 @@ -func @add(%a : !fir.complex<4>, %b : !fir.complex<4>) -> !fir.complex<4> - -func @foo(%a : !fir.complex<4>, %b : !fir.complex<4>) -> !fir.complex<4> { - %1 = call @add(%a, %b) : (!fir.complex<4>, !fir.complex<4>) -> !fir.complex<4> - return %1 : !fir.complex<4> +func @foo(%a : !fir.complex<4>, %b : !fir.complex<4>, %c : !fir.complex<4>, %d : !fir.complex<4>, %e : !fir.complex<4>) -> !fir.complex<4> { + %1 = fir.addc %a, %b : !fir.complex<4> + %2 = fir.mulc %1, %c : !fir.complex<4> + %3 = fir.subc %2, %d : !fir.complex<4> + %4 = fir.divc %3, %e : !fir.complex<4> + return %4 : !fir.complex<4> } diff --git a/test/fir/complex.mlir b/test/fir/complex.mlir new file mode 100644 index 000000000000..5a36992c9567 --- /dev/null +++ b/test/fir/complex.mlir @@ -0,0 +1,6 @@ +func @add(%a : complex, %b : complex) -> complex + +func @foo(%a : complex, %b : complex) -> complex { + %1 = call @add(%a, %b) : (complex, complex) -> complex + return %1 : complex +} From 0ff2505ab544248d0ecd0e2cf2d72970b655d96c Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Wed, 9 Oct 2019 14:55:49 -0700 Subject: [PATCH 012/123] add code to lower addc and subc ops add lowering for mulc and divc --- documentation/FIRLangRef.md | 28 +++++- include/fir/FIROps.td | 3 + lib/fir/Tilikum.cpp | 164 ++++++++++++++++++++++++++++++++---- test/fir/addrof.1.fir | 2 - 4 files changed, 176 insertions(+), 21 deletions(-) diff --git a/documentation/FIRLangRef.md b/documentation/FIRLangRef.md index c097df2ef550..ebf49f110833 100644 --- a/documentation/FIRLangRef.md +++ b/documentation/FIRLangRef.md @@ -847,10 +847,9 @@ Example: Syntax: fir.dispatch method-id ( arg-list ) : func-type - Perform a dynamic dispatch on the method name via the dispatch table -associated with the first argument. - +associated with the first argument. The attribute 'pass_arg_pos' can be +used to select a dispatch argument other than the first one. Example: @@ -864,10 +863,33 @@ The standard dialect does not have primitive operations for complex types. We've added these primitives in the FIR dialect. #### `fir.addc` + +Syntax: fir.addc ssa-value, ssa-value : !fir.complex<k> + +Perform addition of two complex values. The result and arguments must be +the same type. + #### `fir.subc` + +Syntax: fir.subc ssa-value, ssa-value : !fir.complex<k> + +Perform subtraction on complex values. The result and arguments must be the +same type. + #### `fir.mulc` + +Syntax: fir.mulc ssa-value, ssa-value : !fir.complex<k> + +Perform multiplication on complex values. The result and arguments must be +the same type. + #### `fir.divc` +Syntax: fir.divc ssa-value, ssa-value : !fir.complex<k> + +Perform division on complex values. The result and arguments must be the +same type. + ### Other Ops #### `fir.address_of` diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index 77e403c7f1b2..9c101f92f423 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -1604,12 +1604,15 @@ def fir_DispatchOp : fir_Op<"dispatch", []>, llvm::SmallVector argTy(getOperandTypes()); p << " : " << M::FunctionType::get(argTy, resTy, getContext()); }]; + let extraClassDeclaration = [{ 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(); }]; } diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index 475cfcfba4e5..79afe6853549 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -318,6 +318,7 @@ struct BoxAddrOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxaddr = M::cast(op); + // FIXME: stub. needs a test, etc. rewriter.replaceOpWithNewOp(boxaddr, M::Type{}, operands); return matchSuccess(); } @@ -657,6 +658,7 @@ struct FieldIndexOpConversion : public FIROpConversion { } }; +// Replace the fir-end op with a null struct FirEndOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -760,7 +762,7 @@ class GlobalOpConversion : public FIROpConversion { global.getContext()); M::Attribute value; auto addrSpace = /* FIXME: hard-coded i32 here; is that ok? */ - M::IntegerAttr::get(M::IntegerType::get(32, global.getContext()), 0); + rewriter.getI32IntegerAttr(0); rewriter.replaceOpWithNewOp(global, tyAttr, isConst, name, value, addrSpace); return matchSuccess(); @@ -1014,6 +1016,134 @@ struct WhereOpConversion : public FIROpConversion { } }; +// Generate code for complex addition/subtraction +template +M::LLVM::InsertValueOp complexSum(OPTY sumop, + M::ConversionPatternRewriter &rewriter, + FIRToLLVMTypeConverter &lowering) { + auto a = sumop.lhs(); + auto b = sumop.rhs(); + auto loc = sumop.getLoc(); + auto ctx = sumop.getContext(); + auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); + auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); + auto ty = lowering.convertType(sumop.getType()); + auto x = rewriter.create(loc, ty, a, c0); + auto x_ = rewriter.create(loc, ty, b, c0); + auto rx = rewriter.create(loc, ty, x, x_); + auto y = rewriter.create(loc, ty, a, c1); + auto y_ = rewriter.create(loc, ty, b, c1); + auto ry = rewriter.create(loc, ty, y, y_); + auto r = rewriter.create(loc, ty); + auto r_ = rewriter.create(loc, ty, r, rx, c0); + return rewriter.create(loc, ty, r_, ry, c1); +} + +struct AddcOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + // result: (x + x') + i(y + y') + auto addc = cast(op); + auto r = complexSum(addc, rewriter, lowering); + addc.replaceAllUsesWith(r.getResult()); + rewriter.replaceOp(addc, r.getResult()); + return matchSuccess(); + } +}; + +struct SubcOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + // result: (x - x') + i(y - y') + auto subc = M::cast(op); + auto r = complexSum(subc, rewriter, lowering); + subc.replaceAllUsesWith(r.getResult()); + rewriter.replaceOp(subc, r.getResult()); + return matchSuccess(); + } +}; + +struct MulcOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto mulc = M::cast(op); + // TODO: should this just call __muldc3 ? + // result: (xx'-yy')+i(xy'+yx') + auto a = mulc.lhs(); + auto b = mulc.rhs(); + auto loc = mulc.getLoc(); + auto ctx = mulc.getContext(); + auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); + auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); + auto ty = lowering.convertType(mulc.getType()); + auto x = rewriter.create(loc, ty, a, c0); + auto x_ = rewriter.create(loc, ty, b, c0); + auto xx_ = rewriter.create(loc, ty, x, x_); + auto y = rewriter.create(loc, ty, a, c1); + auto yx_ = rewriter.create(loc, ty, y, x_); + auto y_ = rewriter.create(loc, ty, b, c1); + auto xy_ = rewriter.create(loc, ty, x, y_); + auto ri = rewriter.create(loc, ty, xy_, yx_); + auto yy_ = rewriter.create(loc, ty, y, y_); + auto rr = rewriter.create(loc, ty, xx_, yy_); + auto ra = rewriter.create(loc, ty); + auto r_ = rewriter.create(loc, ty, ra, rr, c0); + auto r = rewriter.create(loc, ty, r_, ri, c1); + mulc.replaceAllUsesWith(r.getResult()); + rewriter.replaceOp(mulc, r.getResult()); + return matchSuccess(); + } +}; + +struct DivcOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto divc = M::cast(op); + // TODO: should this just call __divdc3 ? + // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' + auto a = divc.lhs(); + auto b = divc.rhs(); + auto loc = divc.getLoc(); + auto ctx = divc.getContext(); + auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); + auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); + auto ty = lowering.convertType(divc.getType()); + auto x = rewriter.create(loc, ty, a, c0); + auto x_ = rewriter.create(loc, ty, b, c0); + auto xx_ = rewriter.create(loc, ty, x, x_); + auto x_x_ = rewriter.create(loc, ty, x_, x_); + auto y = rewriter.create(loc, ty, a, c1); + auto yx_ = rewriter.create(loc, ty, y, x_); + auto y_ = rewriter.create(loc, ty, b, c1); + auto xy_ = rewriter.create(loc, ty, x, y_); + auto yy_ = rewriter.create(loc, ty, y, y_); + auto y_y_ = rewriter.create(loc, ty, y_, y_); + auto d = rewriter.create(loc, ty, x_x_, y_y_); + auto rrn = rewriter.create(loc, ty, xx_, yy_); + auto rin = rewriter.create(loc, ty, yx_, xy_); + auto rr = rewriter.create(loc, ty, rrn, d); + auto ri = rewriter.create(loc, ty, rin, d); + auto ra = rewriter.create(loc, ty); + auto r_ = rewriter.create(loc, ty, ra, rr, c0); + auto r = rewriter.create(loc, ty, r_, ri, c1); + divc.replaceAllUsesWith(r.getResult()); + rewriter.replaceOp(divc, r.getResult()); + return matchSuccess(); + } +}; + // Lower a SELECT operation into a cascade of conditional branches. The last // case must be the `true` condition. inline void rewriteSelectConstruct(M::Operation *op, OperandTy operands, @@ -1054,22 +1184,24 @@ class FIRToLLVMLoweringPass : public M::ModulePass { FIRToLLVMTypeConverter typeConverter{&context}; M::OwningRewritePatternList patterns; patterns.insert< - AddrOfOpConversion, AllocaOpConversion, AllocMemOpConversion, - BoxAddrOpConversion, BoxCharLenOpConversion, BoxDimsOpConversion, - BoxEleSizeOpConversion, BoxIsAllocOpConversion, BoxIsArrayOpConversion, - BoxIsPtrOpConversion, BoxProcHostOpConversion, BoxRankOpConversion, - BoxTypeDescOpConversion, CallOpConversion, ConvertOpConversion, - CoordinateOpConversion, DispatchOpConversion, DispatchTableOpConversion, - DTEntryOpConversion, EmboxCharOpConversion, EmboxOpConversion, - EmboxProcOpConversion, FirEndOpConversion, ExtractValueOpConversion, - FieldIndexOpConversion, FreeMemOpConversion, GenDimsOpConversion, - GenTypeDescOpConversion, GlobalEntryOpConversion, GlobalOpConversion, - ICallOpConversion, InsertValueOpConversion, LenParamIndexOpConversion, - LoadOpConversion, LoopOpConversion, NoReassocOpConversion, + AddcOpConversion, AddrOfOpConversion, AllocaOpConversion, + AllocMemOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion, + BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion, + BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxProcHostOpConversion, + BoxRankOpConversion, BoxTypeDescOpConversion, CallOpConversion, + ConvertOpConversion, CoordinateOpConversion, DispatchOpConversion, + DispatchTableOpConversion, DivcOpConversion, DTEntryOpConversion, + EmboxCharOpConversion, EmboxOpConversion, EmboxProcOpConversion, + FirEndOpConversion, ExtractValueOpConversion, FieldIndexOpConversion, + FreeMemOpConversion, GenDimsOpConversion, GenTypeDescOpConversion, + GlobalEntryOpConversion, GlobalOpConversion, ICallOpConversion, + InsertValueOpConversion, LenParamIndexOpConversion, LoadOpConversion, + LoopOpConversion, MulcOpConversion, NoReassocOpConversion, SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion, - SelectTypeOpConversion, StoreOpConversion, UnboxCharOpConversion, - UnboxOpConversion, UnboxProcOpConversion, UndefOpConversion, - UnreachableOpConversion, WhereOpConversion>(&context, typeConverter); + SelectTypeOpConversion, StoreOpConversion, SubcOpConversion, + UnboxCharOpConversion, UnboxOpConversion, UnboxProcOpConversion, + UndefOpConversion, UnreachableOpConversion, WhereOpConversion>( + &context, typeConverter); M::populateStdToLLVMConversionPatterns(typeConverter, patterns); M::populateFuncOpTypeConversionPattern(patterns, &context, typeConverter); M::ConversionTarget target{context}; diff --git a/test/fir/addrof.1.fir b/test/fir/addrof.1.fir index f9fd66dd6ed5..75cc12a55eb1 100644 --- a/test/fir/addrof.1.fir +++ b/test/fir/addrof.1.fir @@ -1,5 +1,3 @@ -// Tilikum tests - fir.end is not legalized - fir.global @var_x : !fir.int<4> {} func @getAddressOfX() -> !fir.ref> { From c27b661dbf1afacde829354223657d348039b1b2 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 10 Oct 2019 13:17:40 -0700 Subject: [PATCH 013/123] add code to support FP128 and X86_FP80 add real primitives to FIR to support extended precision types add float primitives for !fir.real types that are not supported in std dialect add option for user control of KIND mappings --- include/fir/FIROps.td | 10 ++ include/fir/KindMapping.h | 68 +++++++++ lib/burnside/bridge.cc | 78 +++++----- lib/burnside/bridge.h | 5 +- lib/burnside/convert-expr.cc | 165 ++++++++++---------- lib/burnside/convert-expr.h | 19 ++- lib/burnside/fe-helper.cc | 235 ++++++++++++++++------------ lib/burnside/fe-helper.h | 54 ++++--- lib/fir/CMakeLists.txt | 1 + lib/fir/Dialect.cpp | 1 - lib/fir/KindMapping.cpp | 221 +++++++++++++++++++++++++++ lib/fir/Tilikum.cpp | 289 +++++++++++++++++++---------------- lib/fir/Type.cpp | 2 +- test/fir/float.fir | 27 ++++ 14 files changed, 791 insertions(+), 384 deletions(-) create mode 100644 include/fir/KindMapping.h create mode 100644 lib/fir/KindMapping.cpp create mode 100644 test/fir/float.fir diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index 9c101f92f423..c51e5f43c94e 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -1633,6 +1633,16 @@ class fir_ArithmeticOp traits = []> : }]; } +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">; + class ComplexArithmeticOp traits = []> : fir_ArithmeticOp, Arguments<(ins fir_ComplexType:$lhs, fir_ComplexType:$rhs)>; diff --git a/include/fir/KindMapping.h b/include/fir/KindMapping.h new file mode 100644 index 000000000000..f5e3ca7cc504 --- /dev/null +++ b/include/fir/KindMapping.h @@ -0,0 +1,68 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIR_KINDMAPPING_H +#define FIR_KINDMAPPING_H + +#include "llvm/IR/Type.h" +#include + +namespace llvm { +template +class Optional; +} // namespace llvm + +namespace mlir { +class MLIRContext; +} // namespace mlir + +namespace fir { + +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); + +private: + MatchResult badMapString(llvm::Twine const &ptr); + MatchResult parse(llvm::StringRef kindMap); + + mlir::MLIRContext *context; + std::map> intMap; + std::map> floatMap; +}; + +} // namespace fir + +#endif // FIR_KINDMAPPING_H diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 516148ff5843..a0968a605c5d 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -30,6 +30,9 @@ #include "mlir/Parser.h" #include "mlir/Target/LLVMIR.h" +#undef TODO +#define TODO() assert(false && "not yet implemented") + namespace Br = Fortran::burnside; namespace Co = Fortran::common; namespace Ev = Fortran::evaluate; @@ -69,6 +72,7 @@ class FIRConverter { M::MLIRContext &mlirContext; const Pa::CookedSource *cooked; M::ModuleOp &module; + Co::IntrinsicTypeDefaultKinds const &defaults; std::unique_ptr builder; LabelMapType blockMap; // map from flattened labels to MLIR blocks std::list edgeQ; @@ -91,31 +95,27 @@ class FIRConverter { /// Construct the type of an Expr
expression M::Type exprType(const SomeExpr *expr) { - return translateSomeExprToFIRType(&mlirContext, expr); + return translateSomeExprToFIRType(&mlirContext, defaults, expr); } M::Type refExprType(const SomeExpr *expr) { - auto type{translateSomeExprToFIRType(&mlirContext, expr)}; + auto type{translateSomeExprToFIRType(&mlirContext, defaults, expr)}; return fir::ReferenceType::get(type); } - int getDefaultIntegerKind() { - return getDefaultKinds().GetDefaultKind(Co::TypeCategory::Integer); - } M::Type getDefaultIntegerType() { - return M::IntegerType::get(8 * getDefaultIntegerKind(), &mlirContext); - } - int getDefaultLogicalKind() { - return getDefaultKinds().GetDefaultKind(Co::TypeCategory::Logical); + return getFIRType(&mlirContext, defaults, IntegerCat); } M::Type getDefaultLogicalType() { - return fir::LogicalType::get(&mlirContext, getDefaultLogicalKind()); + return getFIRType(&mlirContext, defaults, LogicalCat); } M::Value *createFIRAddr(M::Location loc, const SomeExpr *expr) { - return createSomeAddress(loc, build(), *expr, symbolMap, intrinsics); + return createSomeAddress( + loc, build(), *expr, symbolMap, defaults, intrinsics); } M::Value *createFIRExpr(M::Location loc, const SomeExpr *expr) { - return createSomeExpression(loc, build(), *expr, symbolMap, intrinsics); + return createSomeExpression( + loc, build(), *expr, symbolMap, defaults, intrinsics); } M::Value *createTemp(M::Type type, Se::Symbol *symbol = nullptr) { return createTemporary(toLocation(), build(), symbolMap, type, symbol); @@ -262,7 +262,7 @@ class FIRConverter { void pushDoContext(const Pa::NonLabelDoStmt *doStmt, M::Value *doVar = nullptr, M::Value *counter = nullptr, M::Value *stepExpr = nullptr) { - doMap.emplace(doStmt, DoBoundsInfo{doVar, counter, stepExpr}); + doMap.emplace(doStmt, DoBoundsInfo{doVar, counter, stepExpr, 0}); } void genLoopEnterFIR(const Pa::LoopControl::Bounds &bounds, @@ -415,7 +415,7 @@ class FIRConverter { auto callee{genRuntimeFunction( isStopStmt(std::get(stmt.t)) ? FIRT_STOP : FIRT_ERROR_STOP, - getDefaultIntegerKind())}; + defaults.GetDefaultKind(IntegerCat))}; // 2 args: stop-code-opt, quiet-opt llvm::SmallVector operands; build().create(toLocation(), callee, operands); @@ -624,9 +624,9 @@ class FIRConverter { public: FIRConverter(BurnsideBridge &bridge) : mlirContext{bridge.getMLIRContext()}, cooked{bridge.getCookedSource()}, - module{bridge.getModule()}, intrinsics{IntrinsicLibrary::create( - IntrinsicLibrary::Version::LLVM, - mlirContext)} {} + module{bridge.getModule()}, defaults{bridge.getDefaultKinds()}, + intrinsics{IntrinsicLibrary::create( + IntrinsicLibrary::Version::LLVM, mlirContext)} {} FIRConverter() = delete; template constexpr bool Pre(const A &) { return true; } @@ -830,24 +830,23 @@ void FIRConverter::genFIR(const Pa::PauseStmt &stmt) {} /// translate action statements void FIRConverter::genFIR(AnalysisData &ad, const Fl::ActionOp &op) { setCurrentPos(op.v->source); - std::visit( - Co::visitors{ - [](const Pa::ContinueStmt &) { assert(false); }, - [](const Pa::FailImageStmt &) { assert(false); }, - [](const Co::Indirection &) { assert(false); }, - [](const Co::Indirection &) { assert(false); }, - [](const Co::Indirection &) { assert(false); }, - [](const Co::Indirection &) { assert(false); }, - [](const Co::Indirection &) { assert(false); }, - [](const Co::Indirection &) { assert(false); }, - [](const Co::Indirection &) { assert(false); }, - [](const Co::Indirection &) { assert(false); }, - [](const Co::Indirection &) { assert(false); }, - [&](const Co::Indirection &assign) { - genFIR(ad, assign.value()); - }, - [&](const auto &stmt) { genFIR(stmt.value()); }, - }, + std::visit(Co::visitors{ + [](const Pa::ContinueStmt &) { TODO(); }, + [](const Pa::FailImageStmt &) { TODO(); }, + [](const Co::Indirection &) { TODO(); }, + [](const Co::Indirection &) { TODO(); }, + [](const Co::Indirection &) { TODO(); }, + [](const Co::Indirection &) { TODO(); }, + [](const Co::Indirection &) { TODO(); }, + [](const Co::Indirection &) { TODO(); }, + [](const Co::Indirection &) { TODO(); }, + [](const Co::Indirection &) { TODO(); }, + [](const Co::Indirection &) { TODO(); }, + [&](const Co::Indirection &assign) { + genFIR(ad, assign.value()); + }, + [&](const auto &stmt) { genFIR(stmt.value()); }, + }, op.v->statement.u); } @@ -903,13 +902,14 @@ void FIRConverter::translateRoutine( if (funcSym) { if (auto *details{funcSym->detailsIf()}) { for (auto a : details->dummyArgs()) { - auto type{translateSymbolToFIRType(&mlirContext, a)}; + auto type{translateSymbolToFIRType(&mlirContext, defaults, a)}; args.push_back(fir::ReferenceType::get(type)); } if (details->isFunction()) { // FIXME: handle subroutines that return magic values auto *result{&details->result()}; - results.push_back(translateSymbolToFIRType(&mlirContext, result)); + results.push_back( + translateSymbolToFIRType(&mlirContext, defaults, result)); } } else { llvm::errs() << "Symbol: " << funcSym->name().ToString() << " @ " @@ -979,7 +979,3 @@ void Br::instantiateBurnsideBridge( } BurnsideBridge &Br::getBridge() { return *bridgeInstance.get(); } - -const common::IntrinsicTypeDefaultKinds &Br::getDefaultKinds() { - return getBridge().getDefaultKinds(); -} diff --git a/lib/burnside/bridge.h b/lib/burnside/bridge.h index 04af2d250351..b4614a050099 100644 --- a/lib/burnside/bridge.h +++ b/lib/burnside/bridge.h @@ -56,7 +56,7 @@ class BurnsideBridge { void parseSourceFile(llvm::SourceMgr &); - const common::IntrinsicTypeDefaultKinds &getDefaultKinds() { + common::IntrinsicTypeDefaultKinds const &getDefaultKinds() { return defaultKinds; } @@ -88,9 +88,6 @@ void instantiateBurnsideBridge( const common::IntrinsicTypeDefaultKinds &defaultKinds, const parser::CookedSource *cooked = nullptr); -/// access to the default kinds class (for MLIR bridge) -const common::IntrinsicTypeDefaultKinds &getDefaultKinds(); - /// get the burnside bridge singleton BurnsideBridge &getBridge(); diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 77c753938b01..0eb791c2cb6f 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -20,6 +20,7 @@ #include "fir/Type.h" #include "intrinsics.h" #include "runtime.h" +#include "../common/default-kinds.h" #include "../common/unwrap.h" #include "../evaluate/fold.h" #include "../evaluate/real.h" @@ -66,9 +67,10 @@ namespace { class ExprLowering { M::Location location; M::OpBuilder &builder; - const SomeExpr &expr; + SomeExpr const &expr; SymMap &symMap; - const IntrinsicLibrary &intrinsics; + Co::IntrinsicTypeDefaultKinds const &defaults; + IntrinsicLibrary const &intrinsics; M::Location getLoc() { return location; } @@ -105,7 +107,7 @@ class ExprLowering { /// Generate an integral constant of `value` template M::Value *genIntegerConstant(M::MLIRContext *context, std::int64_t value) { - M::Type type{M::IntegerType::get(KIND * 8, context)}; + M::Type type{getFIRType(context, defaults, IntegerCat, 8)}; auto attr{builder.getIntegerAttr(type, value)}; auto res{builder.create(getLoc(), type, attr)}; return res.getResult(); @@ -115,14 +117,14 @@ class ExprLowering { template M::Value *genLogicalConstant(M::MLIRContext *context, bool value) { auto attr{builder.getBoolAttr(value)}; - M::Type logTy{fir::LogicalType::get(context, KIND)}; + M::Type logTy{getFIRType(context, defaults, LogicalCat, KIND)}; auto res{builder.create(getLoc(), logTy, attr)}; return res.getResult(); } template - M::Value *genRealConstant(M::MLIRContext *context, const L::APFloat &value) { - M::Type fltTy{convertReal(KIND, context)}; + M::Value *genRealConstant(M::MLIRContext *context, L::APFloat const &value) { + M::Type fltTy{convertReal(context, KIND)}; auto attr{builder.getFloatAttr(fltTy, value)}; auto res{builder.create(getLoc(), fltTy, attr)}; return res.getResult(); @@ -133,16 +135,16 @@ class ExprLowering { } template - M::Value *createBinaryOp(const A &ex, M::Value *lhs, M::Value *rhs) { + M::Value *createBinaryOp(A const &ex, M::Value *lhs, M::Value *rhs) { assert(lhs && rhs && "argument did not lower"); auto x = builder.create(getLoc(), lhs, rhs); return x.getResult(); } template - M::Value *createBinaryOp(const A &ex, M::Value *rhs) { + M::Value *createBinaryOp(A const &ex, M::Value *rhs) { return createBinaryOp(ex, genval(ex.left()), rhs); } - template M::Value *createBinaryOp(const A &ex) { + template M::Value *createBinaryOp(A const &ex) { return createBinaryOp(ex, genval(ex.left()), genval(ex.right())); } @@ -162,13 +164,14 @@ class ExprLowering { // FIXME binary operation :: ('a, 'a) -> 'a template M::FunctionType createFunctionType() { if constexpr (TC == IntegerCat) { - M::Type output{M::IntegerType::get(KIND, builder.getContext())}; + M::Type output{ + getFIRType(builder.getContext(), defaults, IntegerCat, KIND)}; L::SmallVector inputs; inputs.push_back(output); inputs.push_back(output); return M::FunctionType::get(inputs, output, builder.getContext()); } else if constexpr (TC == RealCat) { - M::Type output{convertReal(KIND, builder.getContext())}; + M::Type output{convertReal(builder.getContext(), KIND)}; L::SmallVector inputs; inputs.push_back(output); inputs.push_back(output); @@ -181,7 +184,7 @@ class ExprLowering { /// Create a call to a Fortran runtime entry point template - M::Value *createBinaryFIRTCall(const A &ex, RuntimeEntryCode callee) { + M::Value *createBinaryFIRTCall(A const &ex, RuntimeEntryCode callee) { L::SmallVector operands; operands.push_back(genval(ex.left())); operands.push_back(genval(ex.right())); @@ -193,58 +196,58 @@ class ExprLowering { template M::Value *createCompareOp( - const A &ex, M::CmpIPredicate pred, M::Value *lhs, M::Value *rhs) { + A const &ex, M::CmpIPredicate pred, M::Value *lhs, M::Value *rhs) { assert(lhs && rhs && "argument did not lower"); auto x = builder.create(getLoc(), pred, lhs, rhs); return x.getResult(); } template - M::Value *createCompareOp(const A &ex, M::CmpIPredicate pred) { + M::Value *createCompareOp(A const &ex, M::CmpIPredicate pred) { return createCompareOp( ex, pred, genval(ex.left()), genval(ex.right())); } template M::Value *createFltCmpOp( - const A &ex, M::CmpFPredicate pred, M::Value *lhs, M::Value *rhs) { + A const &ex, M::CmpFPredicate pred, M::Value *lhs, M::Value *rhs) { assert(lhs && rhs && "argument did not lower"); auto x = builder.create(getLoc(), pred, lhs, rhs); return x.getResult(); } template - M::Value *createFltCmpOp(const A &ex, M::CmpFPredicate pred) { + M::Value *createFltCmpOp(A const &ex, M::CmpFPredicate pred) { return createFltCmpOp( ex, pred, genval(ex.left()), genval(ex.right())); } - M::Value *gen(const Se::Symbol *sym) { + M::Value *gen(Se::Symbol const *sym) { // FIXME: not all symbols are local return createTemporary(getLoc(), builder, symMap, - translateSymbolToFIRType(builder.getContext(), sym), sym); + translateSymbolToFIRType(builder.getContext(), defaults, sym), sym); } - M::Value *gendef(const Se::Symbol *sym) { return gen(sym); } - M::Value *genval(const Se::Symbol *sym) { + M::Value *gendef(Se::Symbol const *sym) { return gen(sym); } + M::Value *genval(Se::Symbol const *sym) { return builder.create(getLoc(), gen(sym)); } - M::Value *genval(const Ev::BOZLiteralConstant &) { TODO(); } - M::Value *genval(const Ev::ProcedureRef &) { TODO(); } - M::Value *genval(const Ev::ProcedureDesignator &) { TODO(); } - M::Value *genval(const Ev::NullPointer &) { TODO(); } - M::Value *genval(const Ev::StructureConstructor &) { TODO(); } - M::Value *genval(const Ev::ImpliedDoIndex &) { TODO(); } - M::Value *genval(const Ev::DescriptorInquiry &) { TODO(); } - template M::Value *genval(const Ev::TypeParamInquiry &) { + M::Value *genval(Ev::BOZLiteralConstant const &) { TODO(); } + M::Value *genval(Ev::ProcedureRef const &) { TODO(); } + M::Value *genval(Ev::ProcedureDesignator const &) { TODO(); } + M::Value *genval(Ev::NullPointer const &) { TODO(); } + M::Value *genval(Ev::StructureConstructor const &) { TODO(); } + M::Value *genval(Ev::ImpliedDoIndex const &) { TODO(); } + M::Value *genval(Ev::DescriptorInquiry const &) { TODO(); } + template M::Value *genval(Ev::TypeParamInquiry const &) { TODO(); } - template M::Value *genval(const Ev::ComplexComponent &) { + template M::Value *genval(Ev::ComplexComponent const &) { TODO(); } template - M::Value *genval(const Ev::Negate> &) { + M::Value *genval(Ev::Negate> const &) { TODO(); } template - M::Value *genval(const Ev::Add> &op) { + M::Value *genval(Ev::Add> const &op) { if constexpr (TC == IntegerCat) { return createBinaryOp(op); } else if constexpr (TC == RealCat) { @@ -254,7 +257,7 @@ class ExprLowering { } } template - M::Value *genval(const Ev::Subtract> &op) { + M::Value *genval(Ev::Subtract> const &op) { if constexpr (TC == IntegerCat) { return createBinaryOp(op); } else if constexpr (TC == RealCat) { @@ -264,7 +267,7 @@ class ExprLowering { } } template - M::Value *genval(const Ev::Multiply> &op) { + M::Value *genval(Ev::Multiply> const &op) { if constexpr (TC == IntegerCat) { return createBinaryOp(op); } else if constexpr (TC == RealCat) { @@ -274,7 +277,7 @@ class ExprLowering { } } template - M::Value *genval(const Ev::Divide> &op) { + M::Value *genval(Ev::Divide> const &op) { if constexpr (TC == IntegerCat) { return createBinaryOp(op); } else if constexpr (TC == RealCat) { @@ -286,7 +289,7 @@ class ExprLowering { } } template - M::Value *genval(const Ev::Power> &op) { + M::Value *genval(Ev::Power> const &op) { if constexpr (TC == IntegerCat) { return createBinaryFIRTCall(op, FIRT_POW); } else { @@ -294,19 +297,19 @@ class ExprLowering { } } template - M::Value *genval(const Ev::RealToIntPower> &) { + M::Value *genval(Ev::RealToIntPower> const &) { TODO(); } - template M::Value *genval(const Ev::ComplexConstructor &) { + template M::Value *genval(Ev::ComplexConstructor const &) { TODO(); } - template M::Value *genval(const Ev::Concat &op) { + template M::Value *genval(Ev::Concat const &op) { return createBinaryFIRTCall(op, FIRT_CONCAT); } /// MIN and MAX operations template - M::Value *genval(const Ev::Extremum> &op) { + M::Value *genval(Ev::Extremum> const &op) { if constexpr (TC == IntegerCat) { return createBinaryFIRTCall( op, op.ordering == Ev::Ordering::Greater ? FIRT_MAX : FIRT_MIN); @@ -315,10 +318,10 @@ class ExprLowering { } } - template M::Value *genval(const Ev::SetLength &) { TODO(); } + template M::Value *genval(Ev::SetLength const &) { TODO(); } template - M::Value *genval(const Ev::Relational> &op) { + M::Value *genval(Ev::Relational> const &op) { if constexpr (TC == IntegerCat) { return createCompareOp(op, translateRelational(op.opr)); } else if constexpr (TC == RealCat) { @@ -327,22 +330,22 @@ class ExprLowering { TODO(); } } - M::Value *genval(const Ev::Relational &op) { + M::Value *genval(Ev::Relational const &op) { return std::visit([&](const auto &x) { return genval(x); }, op.u); } template - M::Value *genval(const Ev::Convert, TC2> &convert) { - auto ty{genTypeFromCategoryAndKind(builder.getContext(), TC1, KIND)}; + M::Value *genval(Ev::Convert, TC2> const &convert) { + auto ty{getFIRType(builder.getContext(), defaults, TC1, KIND)}; return builder.create(getLoc(), ty, genval(convert.left())); } - template M::Value *genval(const Ev::Parentheses &) { TODO(); } + template M::Value *genval(Ev::Parentheses const &) { TODO(); } template M::Value *genval(const Ev::Not &op) { auto *context{builder.getContext()}; return createBinaryOp(op, genLogicalConstant(context, 1)); } - template M::Value *genval(const Ev::LogicalOperation &op) { + template M::Value *genval(Ev::LogicalOperation const &op) { switch (op.logicalOperator) { case Ev::LogicalOperator::And: return createBinaryOp(op); case Ev::LogicalOperator::Or: return createBinaryOp(op); @@ -356,7 +359,7 @@ class ExprLowering { } template - M::Value *genval(const Ev::Constant> &con) { + M::Value *genval(Ev::Constant> const &con) { if constexpr (TC == IntegerCat) { auto opt{con.GetScalarValue()}; if (opt.has_value()) @@ -400,7 +403,7 @@ class ExprLowering { } template - M::Value *genval(const Ev::Constant> &con) { + M::Value *genval(Ev::Constant> const &con) { if constexpr (TC == IntegerCat) { auto opt = (*con).ToInt64(); M::Type type{getSomeKindInteger()}; @@ -413,12 +416,12 @@ class ExprLowering { } } - template M::Value *genval(const Ev::ArrayConstructor &) { + template M::Value *genval(Ev::ArrayConstructor const &) { TODO(); } - M::Value *gen(const Ev::ComplexPart &) { TODO(); } - M::Value *gendef(const Ev::ComplexPart &cp) { return gen(cp); } - M::Value *genval(const Ev::ComplexPart &) { TODO(); } + M::Value *gen(Ev::ComplexPart const &) { TODO(); } + M::Value *gendef(Ev::ComplexPart const &cp) { return gen(cp); } + M::Value *genval(Ev::ComplexPart const &) { TODO(); } M::Value *gen(const Ev::Substring &) { TODO(); } M::Value *gendef(const Ev::Substring &ss) { return gen(ss); } M::Value *genval(const Ev::Substring &) { TODO(); } @@ -437,19 +440,19 @@ class ExprLowering { M::Value *gen(const Ev::DataRef &dref) { return std::visit([&](const auto &x) { return gen(x); }, dref.u); } - M::Value *gendef(const Ev::DataRef &dref) { return gen(dref); } - M::Value *genval(const Ev::DataRef &dref) { + M::Value *gendef(Ev::DataRef const &dref) { return gen(dref); } + M::Value *genval(Ev::DataRef const &dref) { return std::visit([&](const auto &x) { return genval(x); }, dref.u); } // Helper function to turn the left-recursive Component structure into a list. // Returns the object used as the base coordinate for the component chain. - static const Ev::DataRef *reverseComponents( - const Ev::Component &cmpt, std::list &list) { + static Ev::DataRef const *reverseComponents( + Ev::Component const &cmpt, std::list &list) { list.push_front(&cmpt); return std::visit( Co::visitors{ - [&](const Ev::Component &x) { return reverseComponents(x, list); }, + [&](Ev::Component const &x) { return reverseComponents(x, list); }, [&](auto &) { return &cmpt.base(); }, }, cmpt.base().u); @@ -468,7 +471,7 @@ class ExprLowering { coorArgs.push_back(builder.create(getLoc(), name)); } assert(sym && "no component(s)?"); - M::Type ty{translateSymbolToFIRType(builder.getContext(), sym)}; + M::Type ty{translateSymbolToFIRType(builder.getContext(), defaults, sym)}; ty = fir::ReferenceType::get(ty); return builder.create(getLoc(), ty, obj, coorArgs); } @@ -509,7 +512,7 @@ class ExprLowering { } // Return the coordinate of the array reference - M::Value *gen(const Ev::ArrayRef &aref) { + M::Value *gen(Ev::ArrayRef const &aref) { M::Value *base; if (aref.base().IsSymbol()) base = gen(const_cast(&aref.base().GetFirstSymbol())); @@ -530,7 +533,7 @@ class ExprLowering { // Return a coordinate of the coarray reference. This is necessary as a // Component may have a CoarrayRef as its base coordinate. - M::Value *gen(const Ev::CoarrayRef &coref) { + M::Value *gen(Ev::CoarrayRef const &coref) { // FIXME: need to visit the cosubscripts... // return gen(coref.base()); TODO(); @@ -540,13 +543,13 @@ class ExprLowering { return builder.create(getLoc(), gen(coref)); } - template M::Value *gen(const Ev::Designator &des) { + template M::Value *gen(Ev::Designator const &des) { return std::visit([&](const auto &x) { return gen(x); }, des.u); } - template M::Value *gendef(const Ev::Designator &des) { + template M::Value *gendef(Ev::Designator const &des) { return gen(des); } - template M::Value *genval(const Ev::Designator &des) { + template M::Value *genval(Ev::Designator const &des) { return std::visit([&](const auto &x) { return genval(x); }, des.u); } @@ -564,7 +567,7 @@ class ExprLowering { M::Value *genval(const Ev::FunctionRef> &funRef) { if (const auto &intrinsic{funRef.proc().GetSpecificIntrinsic()}) { L::StringRef name{intrinsic->name}; - M::Type ty{genTypeFromCategoryAndKind(builder.getContext(), TC, KIND)}; + M::Type ty{getFIRType(builder.getContext(), defaults, TC, KIND)}; // Probe the intrinsic library if (std::optional func{ intrinsics.getFunction(name, ty, builder)}) { @@ -594,10 +597,10 @@ class ExprLowering { // must be a designator or function-reference (R902) return std::visit([&](const auto &e) { return gendef(e); }, exp.u); } - template M::Value *gendef(const Ev::Expr &exp) { + template M::Value *gendef(Ev::Expr const &exp) { return gen(exp); } - template M::Value *genval(const Ev::Expr &exp) { + template M::Value *genval(Ev::Expr const &exp) { return std::visit([&](const auto &e) { return genval(e); }, exp.u); } @@ -608,8 +611,11 @@ class ExprLowering { public: explicit ExprLowering(M::Location loc, M::OpBuilder &bldr, - const SomeExpr &vop, SymMap &map, const IntrinsicLibrary &intr) - : location{loc}, builder{bldr}, expr{vop}, symMap{map}, intrinsics{intr} {} + SomeExpr const &vop, SymMap &map, + Co::IntrinsicTypeDefaultKinds const &defaults, + IntrinsicLibrary const &intr) + : location{loc}, builder{bldr}, expr{vop}, symMap{map}, defaults{defaults}, + intrinsics{intr} {} /// Lower the expression `expr` into MLIR standard dialect M::Value *gen() { return gen(expr); } @@ -619,26 +625,29 @@ class ExprLowering { } // namespace M::Value *Br::createSomeExpression(M::Location loc, M::OpBuilder &builder, - const Ev::Expr &expr, SymMap &symMap, - const IntrinsicLibrary &intrinsics) { - ExprLowering lower{loc, builder, expr, symMap, intrinsics}; - return lower.genval(); + Ev::Expr const &expr, SymMap &symMap, + Co::IntrinsicTypeDefaultKinds const &defaults, + IntrinsicLibrary const &intrinsics) { + return ExprLowering{loc, builder, expr, symMap, defaults, intrinsics} + .genval(); } M::Value *Br::createSomeAddress(M::Location loc, M::OpBuilder &builder, - const Ev::Expr &expr, SymMap &symMap, - const IntrinsicLibrary &intrinsics) { - ExprLowering lower{loc, builder, expr, symMap, intrinsics}; - return lower.gen(); + Ev::Expr const &expr, SymMap &symMap, + Co::IntrinsicTypeDefaultKinds const &defaults, + IntrinsicLibrary const &intrinsics) { + return ExprLowering{loc, builder, expr, symMap, defaults, intrinsics}.gen(); } /// Create a temporary variable /// `symbol` will be nullptr for an anonymous temporary M::Value *Br::createTemporary(M::Location loc, M::OpBuilder &builder, - SymMap &symMap, M::Type type, const Se::Symbol *symbol) { + SymMap &symMap, M::Type type, Se::Symbol const *symbol) { if (symbol) if (auto *val{symMap.lookupSymbol(symbol)}) { - if (auto *op{val->getDefiningOp()}) return op->getResult(0); + if (auto *op{val->getDefiningOp()}) { + return op->getResult(0); + } return val; } auto insPt(builder.saveInsertionPoint()); diff --git a/lib/burnside/convert-expr.h b/lib/burnside/convert-expr.h index 1a0bd0859ee6..ffbea43bce06 100644 --- a/lib/burnside/convert-expr.h +++ b/lib/burnside/convert-expr.h @@ -24,31 +24,36 @@ class Location; class OpBuilder; class Type; class Value; -} +} // mlir namespace fir { class AllocaExpr; -} +} // fir namespace Fortran { +namespace common { +class IntrinsicTypeDefaultKinds; +} // common namespace evaluate { template class Expr; struct SomeType; } // evaluate namespace semantics { class Symbol; -} +} // semantics namespace burnside { class SymMap; mlir::Value *createSomeExpression(mlir::Location loc, mlir::OpBuilder &builder, - const Fortran::evaluate::Expr &expr, - SymMap &symMap, const IntrinsicLibrary &); + evaluate::Expr const &expr, SymMap &symMap, + common::IntrinsicTypeDefaultKinds const &defaults, + IntrinsicLibrary const &intrinsics); mlir::Value *createSomeAddress(mlir::Location loc, mlir::OpBuilder &builder, - const Fortran::evaluate::Expr &expr, - SymMap &symMap, const IntrinsicLibrary &); + evaluate::Expr const &expr, SymMap &symMap, + common::IntrinsicTypeDefaultKinds const &defaults, + IntrinsicLibrary const &intrinsics); mlir::Value *createTemporary(mlir::Location loc, mlir::OpBuilder &builder, SymMap &symMap, mlir::Type type, const semantics::Symbol *symbol); diff --git a/lib/burnside/fe-helper.cc b/lib/burnside/fe-helper.cc index df385477f290..5c9c0aad23f5 100644 --- a/lib/burnside/fe-helper.cc +++ b/lib/burnside/fe-helper.cc @@ -46,115 +46,158 @@ template int64_t toConstant(const Ev::Expr &e) { } #undef TODO -#define TODO() assert(false) +#define TODO() assert(false); return {} -inline int defaultRealKind() { - return getDefaultKinds().GetDefaultKind(RealCat); +// one argument template, must be specialized +template +M::Type genFIRType(M::MLIRContext *context, int kind) { + return {}; } -inline int defaultIntegerKind() { - return getDefaultKinds().GetDefaultKind(IntegerCat); +// two argument template +template +M::Type genFIRType(M::MLIRContext *context) { + if constexpr (TC == IntegerCat) { + auto bits{Ev::Type::Scalar::bits}; + return M::IntegerType::get(bits, context); + } else if constexpr (TC == LogicalCat || TC == CharacterCat || + TC == ComplexCat) { + return genFIRType(context, KIND); + } else { + return {}; + } } -inline int defaultCharKind() { - return getDefaultKinds().GetDefaultKind(CharacterCat); +template<> M::Type genFIRType(M::MLIRContext *context) { + return M::FloatType::getF16(context); } -inline int defaultLogicalKind() { - return getDefaultKinds().GetDefaultKind(LogicalCat); +template<> M::Type genFIRType(M::MLIRContext *context) { + return M::FloatType::getBF16(context); } -/// Recover the type of an evaluate::Expr and convert it to an -/// mlir::Type. The type returned can be a MLIR standard or FIR type. -class TypeBuilder { - M::MLIRContext *context; +template<> M::Type genFIRType(M::MLIRContext *context) { + return M::FloatType::getF32(context); +} -public: - explicit TypeBuilder(M::MLIRContext *context) : context{context} {} - - /// Create a FIR/MLIR real type - template static M::Type genReal(M::MLIRContext *context) { - if constexpr (KIND == 2) { - return M::FloatType::getF16(context); - } else if constexpr (KIND == 3) { - return M::FloatType::getBF16(context); - } else if constexpr (KIND == 4) { - return M::FloatType::getF32(context); - } else if constexpr (KIND == 8) { - return M::FloatType::getF64(context); - } else { - return fir::RealType::get(context, KIND); +template<> M::Type genFIRType(M::MLIRContext *context) { + return M::FloatType::getF64(context); +} + +template<> M::Type genFIRType(M::MLIRContext *context) { + return fir::RealType::get(context, 10); +} + +template<> M::Type genFIRType(M::MLIRContext *context) { + return fir::RealType::get(context, 16); +} + +template<> M::Type genFIRType(M::MLIRContext *context, int kind) { + if (Ev::IsValidKindOfIntrinsicType(RealCat, kind)) { + switch (kind) { + case 2: return genFIRType(context); + case 3: return genFIRType(context); + case 4: return genFIRType(context); + case 8: return genFIRType(context); + case 10: return genFIRType(context); + case 16: return genFIRType(context); } + assert(false && "type translation not implemented"); } + return {}; +} - template M::Type genReal() { return genReal(context); } - - /// Create a FIR/MLIR real type from information at runtime - static M::Type genReal(int kind, M::MLIRContext *context) { +template<> M::Type genFIRType(M::MLIRContext *context, int kind) { + if (Ev::IsValidKindOfIntrinsicType(IntegerCat, kind)) { switch (kind) { - case 2: return genReal<2>(context); - case 3: return genReal<3>(context); - case 4: return genReal<4>(context); - case 8: return genReal<8>(context); - default: return fir::RealType::get(context, kind); + case 1: return genFIRType(context); + case 2: return genFIRType(context); + case 4: return genFIRType(context); + case 8: return genFIRType(context); + case 16: return genFIRType(context); } + assert(false && "type translation not implemented"); } + return {}; +} + +template<> M::Type genFIRType(M::MLIRContext *context, int KIND) { + if (Ev::IsValidKindOfIntrinsicType(LogicalCat, KIND)) + return fir::LogicalType::get(context, KIND); + return {}; +} + +template<> M::Type genFIRType(M::MLIRContext *context, int KIND) { + if (Ev::IsValidKindOfIntrinsicType(CharacterCat, KIND)) + return fir::CharacterType::get(context, KIND); + return {}; +} + +template<> M::Type genFIRType(M::MLIRContext *context, int KIND) { + if (Ev::IsValidKindOfIntrinsicType(ComplexCat, KIND)) + return fir::CplxType::get(context, KIND); + return {}; +} + +/// Recover the type of an evaluate::Expr and convert it to an +/// mlir::Type. The type returned can be a MLIR standard or FIR type. +class TypeBuilder { + M::MLIRContext *context; + Co::IntrinsicTypeDefaultKinds const &defaults; - M::Type genReal(int kind) { return genReal(kind, context); } + template int defaultKind() { return defaultKind(TC); } + int defaultKind(Co::TypeCategory TC) { return defaults.GetDefaultKind(TC); } + +public: + explicit TypeBuilder( + M::MLIRContext *context, Co::IntrinsicTypeDefaultKinds const &defaults) + : context{context}, defaults{defaults} {} - static M::Type genType(M::MLIRContext *ctxt, Co::TypeCategory tc, int kind) { + // non-template, arguments are runtime values + M::Type genFIRTy(Co::TypeCategory tc, int kind) { switch (tc) { - case IntegerCat: return M::IntegerType::get(kind * 8, ctxt); - case RealCat: return genReal(kind, ctxt); - case ComplexCat: return M::ComplexType::get(genReal(kind, ctxt)); - case CharacterCat: return fir::CharacterType::get(ctxt, kind); - case LogicalCat: return fir::LogicalType::get(ctxt, kind); + case RealCat: return genFIRType(context, kind); + case IntegerCat: return genFIRType(context, kind); + case ComplexCat: return genFIRType(context, kind); + case LogicalCat: return genFIRType(context, kind); + case CharacterCat: return genFIRType(context, kind); default: break; } assert(false && "unhandled type category"); return {}; } - static M::Type genType(M::MLIRContext *ctxt, Co::TypeCategory tc) { - switch (tc) { - case IntegerCat: return genType(ctxt, tc, defaultIntegerKind()); - case RealCat: return genType(ctxt, tc, defaultRealKind()); - case ComplexCat: return genType(ctxt, tc, defaultRealKind()); - case CharacterCat: return genType(ctxt, tc, defaultCharKind()); - case LogicalCat: return genType(ctxt, tc, defaultLogicalKind()); - case DerivedCat: return genType(ctxt, tc, 0); - default: break; - } - assert(false && "unknown type category"); - return {}; + // non-template, category is runtime values, kind is defaulted + M::Type genFIRTy(Co::TypeCategory tc) { + return genFIRTy(tc, defaultKind(tc)); } M::Type gen(const Ev::ImpliedDoIndex &) { - return genType(context, IntegerCat); + return genFIRType(context, defaultKind()); } template typename A, Co::TypeCategory TC> M::Type gen(const A> &) { - return genType(context, TC); + return genFIRType(context, defaultKind()); } template M::Type gen(const Ev::TypeParamInquiry &) { - return genType(context, IntegerCat, KIND); + return genFIRType(context); } template M::Type gen(const Ev::Relational &) { - return fir::LogicalType::get(context, 1); + return genFIRType(context); } template typename A, Co::TypeCategory TC, int KIND> M::Type gen(const A> &) { - return genType(context, TC, KIND); + return genFIRType(context); } // breaks the conflict between A> and Expr deduction template M::Type gen(const Ev::Expr> &) { - return genType(context, TC, KIND); + return genFIRType(context); } template M::Type genVariant(const A &variant) { @@ -192,7 +235,7 @@ class TypeBuilder { if (lbv.has_value() && ubv.has_value() && isConstant(lbv.value()) && isConstant(ubv.value())) { bounds.emplace_back( - toConstant(ubv.value()) - toConstant(lbv.value()) + 1); + toConstant(ubv.value()) - toConstant(lbv.value()) + 1); } else { bounds.emplace_back(0); } @@ -224,24 +267,20 @@ class TypeBuilder { if (auto *tySpec{type->AsIntrinsic()}) { int kind = toConstant(tySpec->kind()); switch (tySpec->category()) { - case IntegerCat: { - returnTy = M::IntegerType::get(kind * 8, context); - } break; - case RealCat: { - returnTy = genReal(kind); - } break; - case ComplexCat: { - returnTy = M::ComplexType::get(genReal(kind)); - } break; - case CharacterCat: { - returnTy = fir::CharacterType::get(context, kind); - } break; - case LogicalCat: { - returnTy = fir::LogicalType::get(context, kind); - } break; - case DerivedCat: { - TODO(); - } break; + case IntegerCat: + returnTy = genFIRType(context, kind); + break; + case RealCat: returnTy = genFIRType(context, kind); break; + case ComplexCat: + returnTy = genFIRType(context, kind); + break; + case CharacterCat: + returnTy = genFIRType(context, kind); + break; + case LogicalCat: + returnTy = genFIRType(context, kind); + break; + case DerivedCat: TODO(); break; } } else if (auto *tySpec{type->AsDerived()}) { TODO(); @@ -313,33 +352,35 @@ M::Location Br::parserPosToLoc(M::MLIRContext &context, return M::UnknownLoc::get(&context); } -M::Type Br::genTypeFromCategoryAndKind( - M::MLIRContext *ctxt, Co::TypeCategory tc, int kind) { - return TypeBuilder::genType(ctxt, tc, kind); +M::Type Br::getFIRType(M::MLIRContext *context, + Co::IntrinsicTypeDefaultKinds const &defaults, Co::TypeCategory tc, + int kind) { + return TypeBuilder{context, defaults}.genFIRTy(tc, kind); } -M::Type Br::genTypeFromCategory(M::MLIRContext *ctxt, Co::TypeCategory tc) { - return TypeBuilder::genType(ctxt, tc); +M::Type Br::getFIRType(M::MLIRContext *context, + Co::IntrinsicTypeDefaultKinds const &defaults, Co::TypeCategory tc) { + return TypeBuilder{context, defaults}.genFIRTy(tc); } -M::Type Br::translateDataRefToFIRType( - M::MLIRContext *context, const Ev::DataRef &dataRef) { - return TypeBuilder{context}.gen(dataRef); +M::Type Br::translateDataRefToFIRType(M::MLIRContext *context, + Co::IntrinsicTypeDefaultKinds const &defaults, const Ev::DataRef &dataRef) { + return TypeBuilder{context, defaults}.gen(dataRef); } // Builds the FIR type from an instance of SomeExpr -M::Type Br::translateSomeExprToFIRType( - M::MLIRContext *context, const SomeExpr *expr) { - return TypeBuilder{context}.gen(*expr); +M::Type Br::translateSomeExprToFIRType(M::MLIRContext *context, + Co::IntrinsicTypeDefaultKinds const &defaults, const SomeExpr *expr) { + return TypeBuilder{context, defaults}.gen(*expr); } // This entry point avoids gratuitously wrapping the Symbol instance in layers // of Expr that will then be immediately peeled back off and discarded. -M::Type Br::translateSymbolToFIRType( - M::MLIRContext *context, const Se::Symbol *symbol) { - return TypeBuilder{context}.gen(symbol); +M::Type Br::translateSymbolToFIRType(M::MLIRContext *context, + Co::IntrinsicTypeDefaultKinds const &defaults, const Se::Symbol *symbol) { + return TypeBuilder{context, defaults}.gen(symbol); } -M::Type Br::convertReal(int KIND, M::MLIRContext *context) { - return TypeBuilder::genReal(KIND, context); +M::Type Br::convertReal(M::MLIRContext *context, int kind) { + return genFIRType(context, kind); } diff --git a/lib/burnside/fe-helper.h b/lib/burnside/fe-helper.h index cbbb4e38fc57..6eebd673280f 100644 --- a/lib/burnside/fe-helper.h +++ b/lib/burnside/fe-helper.h @@ -29,25 +29,30 @@ class MLIRContext; class Type; } -namespace Fortran::evaluate { +namespace Fortran { +namespace common { +class IntrinsicTypeDefaultKinds; +} // common + +namespace evaluate { struct DataRef; template class Designator; template class Expr; template struct SomeKind; struct SomeType; template class Type; -} +} // evaluate -namespace Fortran::parser { +namespace parser { class CharBlock; class CookedSource; -} +} // parser -namespace Fortran::semantics { +namespace semantics { class Symbol; -} +} // semantics -namespace Fortran::burnside { +namespace burnside { using SomeExpr = evaluate::Expr; @@ -64,34 +69,39 @@ mlir::Location dummyLoc(mlir::MLIRContext *ctxt); mlir::Location parserPosToLoc(mlir::MLIRContext &context, const parser::CookedSource *cooked, const parser::CharBlock &position); -mlir::Type genTypeFromCategoryAndKind( - mlir::MLIRContext *ctxt, common::TypeCategory tc, int kind); -mlir::Type genTypeFromCategory( - mlir::MLIRContext *ctxt, common::TypeCategory tc); +mlir::Type getFIRType(mlir::MLIRContext *ctxt, + common::IntrinsicTypeDefaultKinds const &defaults, common::TypeCategory tc, + int kind); +mlir::Type getFIRType(mlir::MLIRContext *ctxt, + common::IntrinsicTypeDefaultKinds const &defaults, common::TypeCategory tc); -mlir::Type translateDataRefToFIRType( - mlir::MLIRContext *ctxt, const evaluate::DataRef &dataRef); +mlir::Type translateDataRefToFIRType(mlir::MLIRContext *ctxt, + common::IntrinsicTypeDefaultKinds const &defaults, + const evaluate::DataRef &dataRef); template inline mlir::Type translateDesignatorToFIRType(mlir::MLIRContext *ctxt, + common::IntrinsicTypeDefaultKinds const &defaults, const evaluate::Designator> &) { - return genTypeFromCategoryAndKind(ctxt, TC, KIND); + return getFIRType(ctxt, defaults, TC, KIND); } template inline mlir::Type translateDesignatorToFIRType(mlir::MLIRContext *ctxt, + common::IntrinsicTypeDefaultKinds const &defaults, const evaluate::Designator> &) { - return genTypeFromCategory(ctxt, TC); + return getFIRType(ctxt, defaults, TC); } -mlir::Type translateSomeExprToFIRType( - mlir::MLIRContext *ctxt, const SomeExpr *expr); - -mlir::Type translateSymbolToFIRType( - mlir::MLIRContext *ctxt, const semantics::Symbol *symbol); +mlir::Type translateSomeExprToFIRType(mlir::MLIRContext *ctxt, + common::IntrinsicTypeDefaultKinds const &defaults, const SomeExpr *expr); -mlir::Type convertReal(int KIND, mlir::MLIRContext *context); +mlir::Type translateSymbolToFIRType(mlir::MLIRContext *ctxt, + common::IntrinsicTypeDefaultKinds const &defaults, + const semantics::Symbol *symbol); -} // Fortran::burnside +mlir::Type convertReal(mlir::MLIRContext *context, int KIND); +} // burnside +} // Fortran #endif // FORTRAN_BURNSIDE_FE_HELPER_H_ diff --git a/lib/fir/CMakeLists.txt b/lib/fir/CMakeLists.txt index 47ae32e73fe8..53a827dec1a0 100644 --- a/lib/fir/CMakeLists.txt +++ b/lib/fir/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(FIR Dialect.cpp FIROps.cpp IteratedDominanceFrontier.cpp + KindMapping.cpp MemToReg.cpp StdConverter.cpp Tilikum.cpp diff --git a/lib/fir/Dialect.cpp b/lib/fir/Dialect.cpp index 507848d5dfc4..decddd87527c 100644 --- a/lib/fir/Dialect.cpp +++ b/lib/fir/Dialect.cpp @@ -13,7 +13,6 @@ // limitations under the License. #include "fir/Dialect.h" -#include "../evaluate/expression.h" #include "fir/Attribute.h" #include "fir/FIROps.h" #include "fir/Type.h" diff --git a/lib/fir/KindMapping.cpp b/lib/fir/KindMapping.cpp new file mode 100644 index 000000000000..f762e75cc0e7 --- /dev/null +++ b/lib/fir/KindMapping.cpp @@ -0,0 +1,221 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fir/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. + +namespace M = mlir; + +using namespace fir; +using namespace llvm; + +using Bitsize = KindMapping::Bitsize; +using KindTy = KindMapping::KindTy; +using LLVMTypeID = KindMapping::LLVMTypeID; +using MatchResult = KindMapping::MatchResult; + +static cl::opt + ClKindMapping("kind-mapping", + cl::desc("kind mapping string to set kind precision"), + cl::value_desc("kind-mapping-string"), cl::init("")); + +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; + } +} + +template +RT doLookup(std::function def, + std::map> const &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); +} + +template +Bitsize getIntegerLikeBitsize(KindTy kind, MAP const &map) { + return doLookup(defaultScalingKind, map, kind); +} + +template +LLVMTypeID getFloatLikeTypeID(KindTy kind, MAP const &map) { + return doLookup(defaultRealKind, map, kind); +} + +MatchResult parseCode(char &code, char const *&ptr) { + if (*ptr != 'a' && *ptr != 'c' && *ptr != 'i' && *ptr != 'l' && *ptr != 'r') + return {}; + code = *ptr++; + return {true}; +} + +template +MatchResult parseSingleChar(char const *&ptr) { + if (*ptr != ch) + return {}; + ++ptr; + return {true}; +} + +MatchResult parseColon(char const *&ptr) { return parseSingleChar<':'>(ptr); } + +MatchResult parseComma(char const *&ptr) { return parseSingleChar<','>(ptr); } + +MatchResult parseInt(unsigned &result, char const *&ptr) { + char const *beg = ptr; + while (*ptr >= '0' && *ptr <= '9') + ptr++; + if (beg == ptr) + return {}; + StringRef ref(beg, ptr - beg); + int temp; + if (ref.consumeInteger(10, temp)) + return {}; + result = temp; + return {true}; +} + +bool matchString(char const *&ptr, StringRef literal) { + StringRef s(ptr); + if (s.startswith(literal)) { + ptr += literal.size(); + return true; + } + return false; +} + +MatchResult parseTypeID(LLVMTypeID &result, char const *&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 + +fir::KindMapping::KindMapping(mlir::MLIRContext *context, StringRef map) + : context{context} { + parse(map); +} + +fir::KindMapping::KindMapping(mlir::MLIRContext *context) + : KindMapping{context, ClKindMapping} {} + +MatchResult fir::KindMapping::badMapString(Twine const &ptr) { + auto unknown = mlir::UnknownLoc::get(context); + mlir::emitError(unknown, ptr); + return {}; +} + +MatchResult fir::KindMapping::parse(StringRef kindMap) { + if (kindMap.empty()) + return {true}; + char const *srcPtr = kindMap.begin(); + while (true) { + char code; + KindTy kind; + if (parseCode(code, srcPtr) || parseInt(kind, srcPtr)) + return badMapString(srcPtr); + if (code == 'a' || code == 'i' || code == 'l') { + Bitsize bits; + 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 fir::KindMapping::getCharacterBitsize(KindTy kind) { + return getIntegerLikeBitsize<'a'>(kind, intMap); +} + +Bitsize fir::KindMapping::getIntegerBitsize(KindTy kind) { + return getIntegerLikeBitsize<'i'>(kind, intMap); +} + +Bitsize fir::KindMapping::getLogicalBitsize(KindTy kind) { + return getIntegerLikeBitsize<'l'>(kind, intMap); +} + +LLVMTypeID fir::KindMapping::getRealTypeID(KindTy kind) { + return getFloatLikeTypeID<'r'>(kind, floatMap); +} + +LLVMTypeID fir::KindMapping::getComplexTypeID(KindTy kind) { + return getFloatLikeTypeID<'c'>(kind, floatMap); +} diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index 79afe6853549..0ff89647a55b 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -36,6 +36,9 @@ /// The bridge that performs the conversion of FIR and standard dialect /// operations to the LLVM-IR dialect. +#undef TODO +#define TODO(X) (void)X; assert(false && "not yet implemented") + namespace L = llvm; namespace M = mlir; @@ -136,15 +139,9 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { case 8: return M::LLVM::LLVMType::getDoubleTy(llvmDialect); case 10: - // return M::LLVM::LLVMType::get(ctx, L::Type::getX86_FP80Ty(llvmCtx)); - emitError(UnknownLoc::get(mlirContext), - "unsupported type: !fir.real<10>"); - return {}; + return M::LLVM::LLVMType::getX86_FP80Ty(llvmDialect); case 16: - // return M::LLVM::LLVMType::get(ctx, L::Type::getFP128Ty(llvmCtx)); - emitError(UnknownLoc::get(mlirContext), - "unsupported type: !fir.real<16>"); - return {}; + return M::LLVM::LLVMType::getFP128Ty(llvmDialect); } emitError(UnknownLoc::get(mlirContext)) << "unsupported type: !fir.real<" << kind << ">"; @@ -152,8 +149,7 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { } M::LLVM::LLVMType convertRecordType(RecordType derived) { - // FIXME: implement - assert(false); + TODO(0); return {}; } @@ -331,8 +327,7 @@ struct BoxCharLenOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxchar = M::cast(op); - // TODO - assert(false && boxchar); + TODO(boxchar); return matchSuccess(); } }; @@ -344,8 +339,7 @@ struct BoxDimsOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxdims = M::cast(op); - // TODO - assert(false && boxdims); + TODO(boxdims); return matchSuccess(); } }; @@ -357,8 +351,7 @@ struct BoxEleSizeOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxelesz = M::cast(op); - // TODO - assert(false && boxelesz); + TODO(boxelesz); return matchSuccess(); } }; @@ -370,8 +363,7 @@ struct BoxIsAllocOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxisalloc = M::cast(op); - // TODO - assert(false && boxisalloc); + TODO(boxisalloc); return matchSuccess(); } }; @@ -383,8 +375,7 @@ struct BoxIsArrayOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxisarray = M::cast(op); - // TODO - assert(false && boxisarray); + TODO(boxisarray); return matchSuccess(); } }; @@ -396,8 +387,7 @@ struct BoxIsPtrOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxisptr = M::cast(op); - // TODO - assert(false && boxisptr); + TODO(boxisptr); return matchSuccess(); } }; @@ -409,8 +399,7 @@ struct BoxProcHostOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxprochost = M::cast(op); - // TODO - assert(false && boxprochost); + TODO(boxprochost); return matchSuccess(); } }; @@ -422,8 +411,7 @@ struct BoxRankOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxrank = M::cast(op); - // TODO - assert(false && boxrank); + TODO(boxrank); return matchSuccess(); } }; @@ -435,8 +423,7 @@ struct BoxTypeDescOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxtypedesc = M::cast(op); - // TODO - assert(false && boxtypedesc); + TODO(boxtypedesc); return matchSuccess(); } }; @@ -448,8 +435,7 @@ struct CallOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto call = M::cast(op); - // TODO - assert(false && call); + TODO(call); return matchSuccess(); } }; @@ -462,28 +448,31 @@ struct ConvertOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto convert = M::cast(op); - M::Type fromTy = lowering.convertType(convert.value()->getType()); - M::Type toTy = lowering.convertType(convert.res()->getType()); - auto loc = op->getLoc(); - M::Value *op0 = operands[0]; - auto *fromLLVMTy = fromTy.cast().getUnderlyingType(); - auto *toLLVMTy = fromTy.cast().getUnderlyingType(); - M::Value *v = nullptr; + auto fromTy_ = lowering.convertType(convert.value()->getType()); + auto fromTy = lowering.unwrap(fromTy_); + auto toTy_ = lowering.convertType(convert.res()->getType()); + auto toTy = lowering.unwrap(toTy_); + auto *fromLLVMTy = fromTy.getUnderlyingType(); + auto *toLLVMTy = toTy.getUnderlyingType(); + auto *op0 = operands[0]; if (fromLLVMTy == toLLVMTy) { - rewriter.replaceOp(op, op0); + rewriter.replaceOp(convert, op0); return matchSuccess(); } + auto loc = convert.getLoc(); + M::Value *v = {}; if (fromLLVMTy->isFloatingPointTy()) { - if (toLLVMTy->isIntegerTy()) { - v = rewriter.create(loc, toTy, op0); - } else if (toLLVMTy->isFloatingPointTy()) { - unsigned fromBits = fromLLVMTy->getIntegerBitWidth(); - unsigned toBits = toLLVMTy->getIntegerBitWidth(); + if (toLLVMTy->isFloatingPointTy()) { + unsigned fromBits = fromLLVMTy->getPrimitiveSizeInBits(); + unsigned toBits = toLLVMTy->getPrimitiveSizeInBits(); + // TODO: what if different reps (F16, BF16) are the same size? assert(fromBits != toBits); if (fromBits > toBits) v = rewriter.create(loc, toTy, op0); else v = rewriter.create(loc, toTy, op0); + } else if (toLLVMTy->isIntegerTy()) { + v = rewriter.create(loc, toTy, op0); } } else if (fromLLVMTy->isIntegerTy()) { if (toLLVMTy->isIntegerTy()) { @@ -500,27 +489,16 @@ struct ConvertOpConversion : public FIROpConversion { v = rewriter.create(loc, toTy, op0); } } else if (fromLLVMTy->isPointerTy()) { - if (toLLVMTy->isIntegerTy()) + if (toLLVMTy->isIntegerTy()) { v = rewriter.create(loc, toTy, op0); - } - if (v == nullptr) { - v = rewriter.create(loc, toTy, op0); - } - - if (auto fromInt = fromTy.dyn_cast()) { - if (auto toInt = toTy.dyn_cast()) { - M::Value *v; - if (fromInt.getIntOrFloatBitWidth() < toInt.getIntOrFloatBitWidth()) { - v = rewriter.create(loc, toInt, op0); - } else { - v = rewriter.create(loc, toInt, op0); - } - rewriter.replaceOp(op, v); - return matchSuccess(); + } else if (toLLVMTy->isPointerTy()) { + v = rewriter.create(loc, toTy, op0); } - // FIXME -- finish implementation } - assert(false); + if (v) + rewriter.replaceOp(op, v); + else + emitError(loc) << "cannot convert " << fromTy_ << " to " << toTy_; return matchSuccess(); } }; @@ -542,7 +520,7 @@ struct CoordinateOpConversion : public FIROpConversion { rewriter.replaceOp(op, v); return matchSuccess(); } - assert(false && coor); // FIXME + TODO(coor); return matchSuccess(); } }; @@ -555,8 +533,7 @@ struct DispatchOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto dispatch = M::cast(op); - // TODO - assert(false && dispatch); + TODO(dispatch); return matchSuccess(); } }; @@ -569,8 +546,7 @@ struct DispatchTableOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto disptable = M::cast(op); - // TODO - assert(false && disptable); + TODO(disptable); return matchSuccess(); } }; @@ -583,8 +559,7 @@ struct DTEntryOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto dtentry = M::cast(op); - // TODO - assert(false && dtentry); + TODO(dtentry); return matchSuccess(); } }; @@ -597,8 +572,7 @@ struct EmboxCharOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto emboxchar = M::cast(op); - // TODO - assert(false && emboxchar); + TODO(emboxchar); return matchSuccess(); } }; @@ -611,8 +585,7 @@ struct EmboxOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto embox = M::cast(op); - // TODO - assert(false && embox); + TODO(embox); return matchSuccess(); } }; @@ -625,8 +598,7 @@ struct EmboxProcOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto emboxproc = M::cast(op); - // TODO - assert(false && emboxproc); + TODO(emboxproc); return matchSuccess(); } }; @@ -639,8 +611,7 @@ struct ExtractValueOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto extractVal = M::cast(op); - // FIXME - assert(false && extractVal); + TODO(extractVal); return matchSuccess(); } }; @@ -652,8 +623,7 @@ struct FieldIndexOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto fieldindex = M::cast(op); - // TODO - assert(false && fieldindex); + TODO(fieldindex); return matchSuccess(); } }; @@ -712,8 +682,7 @@ struct GenDimsOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto gendims = M::cast(op); - // TODO - assert(false && gendims); + TODO(gendims); return matchSuccess(); } }; @@ -725,8 +694,7 @@ struct GenTypeDescOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto gentypedesc = M::cast(op); - // TODO - assert(false && gentypedesc); + TODO(gentypedesc); return matchSuccess(); } }; @@ -738,8 +706,7 @@ struct GlobalEntryOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto globalentry = M::cast(op); - // TODO - assert(false && globalentry); + TODO(globalentry); return matchSuccess(); } }; @@ -777,8 +744,7 @@ struct ICallOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto icall = M::cast(op); - // TODO - assert(false && icall); + TODO(icall); return matchSuccess(); } }; @@ -790,12 +756,13 @@ struct InsertValueOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto insertVal = cast(op); - // FIXME - assert(false && insertVal); + TODO(insertVal); + // rewriter.replaceOpWithNewOp(insertVal, ...); return matchSuccess(); } }; +// compute the index of the LEN param in the descriptor addendum struct LenParamIndexOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -804,8 +771,7 @@ struct LenParamIndexOpConversion matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto lenparam = M::cast(op); - // TODO - assert(false && lenparam); + TODO(lenparam); return matchSuccess(); } }; @@ -836,8 +802,7 @@ struct LoopOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto loop = M::cast(op); - // TODO - assert(false && loop); + TODO(loop); return matchSuccess(); } }; @@ -849,8 +814,7 @@ struct NoReassocOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto noreassoc = M::cast(op); - // FIXME - assert(false && noreassoc); + TODO(noreassoc); return matchSuccess(); } }; @@ -864,8 +828,7 @@ struct SelectCaseOpConversion : public FIROpConversion { L::ArrayRef destOperands, M::ConversionPatternRewriter &rewriter) const override { auto selectcase = M::cast(op); - // FIXME - assert(false && selectcase); + TODO(selectcase); return matchSuccess(); } }; @@ -880,8 +843,7 @@ struct SelectOpConversion : public FIROpConversion { L::ArrayRef destOperands, M::ConversionPatternRewriter &rewriter) const override { auto select = M::cast(op); - // FIXME - assert(false && select); + TODO(select); return matchSuccess(); } }; @@ -895,12 +857,12 @@ struct SelectRankOpConversion : public FIROpConversion { L::ArrayRef destOperands, M::ConversionPatternRewriter &rewriter) const override { auto selectrank = M::cast(op); - // FIXME - assert(false && selectrank); + TODO(selectrank); return matchSuccess(); } }; +// SelectTypeOp should have already been lowered struct SelectTypeOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -910,8 +872,7 @@ struct SelectTypeOpConversion : public FIROpConversion { L::ArrayRef destOperands, M::ConversionPatternRewriter &rewriter) const override { auto selecttype = M::cast(op); - // FIXME - assert(false && selecttype); + TODO(selecttype); return matchSuccess(); } }; @@ -938,8 +899,8 @@ struct UnboxCharOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto unboxchar = M::cast(op); - // TODO - assert(false && unboxchar); + unboxchar.replaceAllUsesWith(operands); + rewriter.replaceOp(unboxchar, {}); return matchSuccess(); } }; @@ -952,8 +913,8 @@ struct UnboxOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto unbox = M::cast(op); - // TODO - assert(false && unbox); + unbox.replaceAllUsesWith(operands); + rewriter.replaceOp(unbox, {}); return matchSuccess(); } }; @@ -966,8 +927,8 @@ struct UnboxProcOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto unboxproc = M::cast(op); - // TODO - assert(false && unboxproc); + unboxproc.replaceAllUsesWith(operands); + rewriter.replaceOp(unboxproc, {}); return matchSuccess(); } }; @@ -1010,8 +971,7 @@ struct WhereOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto where = M::cast(op); - // TODO - assert(false && where); + TODO(where); return matchSuccess(); } }; @@ -1039,6 +999,71 @@ M::LLVM::InsertValueOp complexSum(OPTY sumop, return rewriter.create(loc, ty, r_, ry, c1); } +template +void lowerRealBinaryOp(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter, + FIRToLLVMTypeConverter &lowering) { + auto binop = cast(op); + auto ty = lowering.convertType(binop.getType()); + rewriter.replaceOpWithNewOp(binop, ty, operands); +} + +struct AddfOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + lowerRealBinaryOp(op, operands, rewriter, + lowering); + return matchSuccess(); + } +}; +struct SubfOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + lowerRealBinaryOp(op, operands, rewriter, + lowering); + return matchSuccess(); + } +}; +struct MulfOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + lowerRealBinaryOp(op, operands, rewriter, + lowering); + return matchSuccess(); + } +}; +struct DivfOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + lowerRealBinaryOp(op, operands, rewriter, + lowering); + return matchSuccess(); + } +}; +struct ModfOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + lowerRealBinaryOp(op, operands, rewriter, + lowering); + return matchSuccess(); + } +}; + struct AddcOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -1184,31 +1209,29 @@ class FIRToLLVMLoweringPass : public M::ModulePass { FIRToLLVMTypeConverter typeConverter{&context}; M::OwningRewritePatternList patterns; patterns.insert< - AddcOpConversion, AddrOfOpConversion, AllocaOpConversion, - AllocMemOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion, - BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion, - BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxProcHostOpConversion, - BoxRankOpConversion, BoxTypeDescOpConversion, CallOpConversion, - ConvertOpConversion, CoordinateOpConversion, DispatchOpConversion, - DispatchTableOpConversion, DivcOpConversion, DTEntryOpConversion, - EmboxCharOpConversion, EmboxOpConversion, EmboxProcOpConversion, - FirEndOpConversion, ExtractValueOpConversion, FieldIndexOpConversion, - FreeMemOpConversion, GenDimsOpConversion, GenTypeDescOpConversion, - GlobalEntryOpConversion, GlobalOpConversion, ICallOpConversion, - InsertValueOpConversion, LenParamIndexOpConversion, LoadOpConversion, - LoopOpConversion, MulcOpConversion, NoReassocOpConversion, - SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion, - SelectTypeOpConversion, StoreOpConversion, SubcOpConversion, - UnboxCharOpConversion, UnboxOpConversion, UnboxProcOpConversion, - UndefOpConversion, UnreachableOpConversion, WhereOpConversion>( - &context, typeConverter); + AddcOpConversion, AddfOpConversion, AddrOfOpConversion, + AllocaOpConversion, AllocMemOpConversion, BoxAddrOpConversion, + BoxCharLenOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion, + BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion, + BoxProcHostOpConversion, BoxRankOpConversion, BoxTypeDescOpConversion, + CallOpConversion, ConvertOpConversion, CoordinateOpConversion, + DispatchOpConversion, DispatchTableOpConversion, DivcOpConversion, + DivfOpConversion, DTEntryOpConversion, EmboxCharOpConversion, + EmboxOpConversion, EmboxProcOpConversion, FirEndOpConversion, + ExtractValueOpConversion, FieldIndexOpConversion, FreeMemOpConversion, + GenDimsOpConversion, GenTypeDescOpConversion, GlobalEntryOpConversion, + GlobalOpConversion, ICallOpConversion, InsertValueOpConversion, + LenParamIndexOpConversion, LoadOpConversion, LoopOpConversion, + ModfOpConversion, MulcOpConversion, MulfOpConversion, + NoReassocOpConversion, SelectCaseOpConversion, SelectOpConversion, + SelectRankOpConversion, SelectTypeOpConversion, StoreOpConversion, + SubcOpConversion, SubfOpConversion, UnboxCharOpConversion, + UnboxOpConversion, UnboxProcOpConversion, UndefOpConversion, + UnreachableOpConversion, WhereOpConversion>(&context, typeConverter); M::populateStdToLLVMConversionPatterns(typeConverter, patterns); - M::populateFuncOpTypeConversionPattern(patterns, &context, typeConverter); M::ConversionTarget target{context}; target.addLegalDialect(); - target.addDynamicallyLegalOp([&](M::FuncOp op) { - return typeConverter.isSignatureLegal(op.getType()); - }); + // required NOP stubs for applying a full conversion target.addDynamicallyLegalOp( [&](M::ModuleOp op) { return true; }); @@ -1230,7 +1253,7 @@ struct LLVMIRLoweringPass : public M::ModulePass { void runOnModule() override { if (auto llvmModule{M::translateModuleToLLVMIR(getModule())}) { std::error_code ec; - auto stream{L::raw_fd_ostream("a.ll", ec, L::sys::fs::F_None)}; + L::raw_fd_ostream stream("a.ll", ec, L::sys::fs::F_None); stream << *llvmModule << '\n'; } else { auto ctxt{getModule().getContext()}; diff --git a/lib/fir/Type.cpp b/lib/fir/Type.cpp index 70a861ba9961..834b7408d610 100644 --- a/lib/fir/Type.cpp +++ b/lib/fir/Type.cpp @@ -651,7 +651,7 @@ FIRTypeParser::verifyDerived(RecordType derivedTy, emitError(loc, "field parameter has invalid type"); return {}; } - llvm::StringSet uniq; + llvm::StringSet<> uniq; for (auto &p : lenPList) if (!uniq.insert(p.first).second) { emitError(loc, "LEN parameter cannot have duplicate name"); diff --git a/test/fir/float.fir b/test/fir/float.fir new file mode 100644 index 000000000000..f12b1383d6ab --- /dev/null +++ b/test/fir/float.fir @@ -0,0 +1,27 @@ +func @bar(%a : !fir.real<2>, %b : !fir.real<4>, %c : !fir.real<8>, %d : !fir.real<10>, %e : !fir.real<16>) -> !fir.real<10> { + %1 = fir.convert %a : (!fir.real<2>) -> !fir.real<10> + %2 = fir.convert %b : (!fir.real<4>) -> !fir.real<10> + %3 = fir.convert %c : (!fir.real<8>) -> !fir.real<10> + %4 = fir.convert %d : (!fir.real<10>) -> !fir.real<10> + %5 = fir.convert %e : (!fir.real<16>) -> !fir.real<10> + %6 = call @foo2(%1, %2, %3, %4, %5) : (!fir.real<10>, !fir.real<10>, !fir.real<10>, !fir.real<10>, !fir.real<10>) -> !fir.real<10> + return %6 : !fir.real<10> +} + +func @foo(%a : !fir.real<16>, %b : !fir.real<16>, %c : !fir.real<16>, %d : !fir.real<16>, %e : !fir.real<16>) -> !fir.real<16> { + %1 = fir.addf %a, %b : !fir.real<16> + %2 = fir.mulf %1, %c : !fir.real<16> + %3 = fir.subf %2, %d : !fir.real<16> + %4 = fir.divf %3, %e : !fir.real<16> + %5 = fir.modf %4, %a : !fir.real<16> + return %5 : !fir.real<16> +} + +func @foo2(%a : !fir.real<10>, %b : !fir.real<10>, %c : !fir.real<10>, %d : !fir.real<10>, %e : !fir.real<10>) -> !fir.real<10> { + %1 = fir.addf %a, %b : !fir.real<10> + %2 = fir.mulf %1, %c : !fir.real<10> + %3 = fir.subf %2, %d : !fir.real<10> + %4 = fir.divf %3, %e : !fir.real<10> + %5 = fir.modf %4, %a : !fir.real<10> + return %5 : !fir.real<10> +} From a22fa239a2d3f60b33f49ad3fd6936e495643931 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Thu, 10 Oct 2019 10:52:34 -0700 Subject: [PATCH 014/123] Refactor intrinsic function lowering facility + Add a level of indirection before the facility that describes the runtime functions that implement Fortran intrinsic functions. This level allows to select a code generation function depending on the intrinsic name. This is because not all Fortran intrinsic functions map to runtime functions and even when they do, they may not map one to one and may require generating some boilerplate. + Completly hide the implementation using an owning pointer. It did not bring anything to define implemenation related class in the header. So only expose a simple interface. + All code implementation code is reorganised in three classes: -> RuntimeStaticDescription: allows constexpr description of the runtime libraries. -> MathRuntimeLibrary: maps a reified representation of runtime functions to generic intrinsic names. It is dynamically built from RuntimeStaticDescription. -> IntrinsicLibrary::Implementation: define functions to generate FIR+MLIR for intrinsic functions. Defines a dispatch table between generic names and such generator functions. Holds a MathRuntimeLibrary to allow generator function to generate runtime calls. + Get rid of non constexpr global objects and make IntrinsicLibrary immutable once it is built. This is to harden this utility for use in multithreaded contexts. + Add the possibility to generate FIR+MLIR code for intrinsic calls inside wrappers to keep the code readable and also keep better track of Fortran intrinsics which may be usefull/necessary in future optimization passes. Address review comments * Type -> TypeCode to avoid confusion with mlir::Type * Remove resultType member from IntrinsicLibrary::Implementation::Context * std::optional -> llvm::Optional --- lib/burnside/convert-expr.cc | 29 +-- lib/burnside/intrinsics.cc | 456 ++++++++++++++++++++++++++++++----- lib/burnside/intrinsics.h | 70 +++--- 3 files changed, 443 insertions(+), 112 deletions(-) diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 0eb791c2cb6f..bdbe9f0105ed 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -566,28 +566,19 @@ class ExprLowering { template M::Value *genval(const Ev::FunctionRef> &funRef) { if (const auto &intrinsic{funRef.proc().GetSpecificIntrinsic()}) { - L::StringRef name{intrinsic->name}; M::Type ty{getFIRType(builder.getContext(), defaults, TC, KIND)}; - // Probe the intrinsic library - if (std::optional func{ - intrinsics.getFunction(name, ty, builder)}) { - L::SmallVector operands; - for (const auto &arg : funRef.arguments()) { - if (auto *expr{Ev::UnwrapExpr>(arg)}) { - operands.push_back(genval(*expr)); - } else { - // Some intrinsics have optional arguments (inquiry, - // transformational). These kind of intrinsics cannot be blindly - // mapped 1 to 1 to a runtime signature and will require a more - // ad-hoc handling. - TODO(); - } + L::SmallVector operands; + // Lower arguments + for (const auto &arg : funRef.arguments()) { + if (auto *expr{Ev::UnwrapExpr>(arg)}) { + operands.push_back(genval(*expr)); + } else { + operands.push_back(nullptr); // optional } - auto x{builder.create(getLoc(), *func, operands)}; - return x.getResult(0); // FIXME - } else { - TODO(); // No runtime function for this intrinsic. } + // Let the intrinsic library lower the intrinsic function call + L::StringRef name{intrinsic->name}; + return intrinsics.genval(getLoc(), builder, name, ty, operands); } else { TODO(); // User defined function. } diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index 3dc6cc5388e1..e49732cd4ba3 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -14,85 +14,423 @@ #include "intrinsics.h" #include "builder.h" +#include "fir/FIROps.h" +#include "llvm/ADT/Optional.h" +#include +#include /// [Coding style](https://llvm.org/docs/CodingStandards.html) namespace Fortran::burnside { -// Define a simple static runtime description that will be transformed into -// IntrinsicImplementation when building the IntrinsicLibrary. -namespace runtime { -enum class Type { f32, f64 }; -struct StaticDescription { - const char *name; - const char *symbol; - Type resultType; - std::vector argumentTypes; +/// MathRuntimeLibrary maps Fortran generic intrinsic names to runtime function +/// signatures. There is no guarantee that that runtime functions are available +/// for all intrinsic functions and possible types. +/// To be easy and fast to use, this class holds a map and uses +/// mlir::FunctionType to represent the runtime function type. This imply that +/// MathRuntimeLibrary cannot be constexpr built and requires an +/// mlir::MLIRContext to be built. Its constructor uses a constexpr table +/// description of the runtime. The runtime functions are not declared into the +/// mlir::module until there is a query that needs them. This is to avoid +/// polluting the FIR/LLVM IR dumps with unused functions. +class MathRuntimeLibrary { +public: + /// The map key are Fortran generic intrinsic names. + using Key = llvm::StringRef; + struct Hash { // Need custom hash for this kind of key + size_t operator()(const Key &k) const { return llvm::hash_value(k); } + }; + /// Runtime function description that is sufficient to build an + /// mlir::FuncOp and to compare function types. + struct RuntimeFunction { + RuntimeFunction(llvm::StringRef n, mlir::FunctionType t) + : symbol{n}, type{t} {}; + llvm::StringRef symbol; + mlir::FunctionType type; + }; + using Map = std::unordered_multimap; + + MathRuntimeLibrary(IntrinsicLibrary::Version, mlir::MLIRContext &); + + /// Probe the intrinsic library for a certain intrinsic and get/build the + /// related mlir::FuncOp if a runtime description is found. + /// Also add a unit attribute "fir.runtime" to the function so that later + /// it is possible to quickly know what function are intrinsics vs users. + llvm::Optional getFunction( + mlir::OpBuilder &, llvm::StringRef, mlir::FunctionType) const; + +private: + mlir::FuncOp getFuncOp( + mlir::OpBuilder &builder, const RuntimeFunction &runtime) const; + Map library; +}; + +/// The implementation of IntrinsicLibrary is based on a map that associates +/// Fortran intrinsics generic names to the related FIR generator functions. +/// All generator functions are member functions of the Implementation class +/// and they all take the same context argument that contains the name and +/// arguments of the Fortran intrinsics call to lower among other things. +/// A same FIR generator function may be able to generate the FIR for several +/// intrinsics. For instance generateRuntimeCall tries to find a runtime +/// functions that matches the Fortran intrinsic call and generate the +/// operations to call this functions if it was found. +/// IntrinsicLibrary holds a constant MathRuntimeLibrary that it uses to +/// find and place call to math runtime functions. This library is built +/// when the Implementation is built. Because of this, Implementation is +/// not cheap to build and it should be kept as long as possible. + +// TODO it is unclear how optional argument are handled +// TODO error handling -> return a code or directly emit messages ? +class IntrinsicLibrary::Implementation { +public: + Implementation(Version v, mlir::MLIRContext &c) : runtime{v, c} {} + inline mlir::Value *genval(mlir::Location loc, mlir::OpBuilder &builder, + llvm::StringRef name, mlir::Type resultType, + llvm::ArrayRef args) const; + +private: + // Info needed by Generators is passed in Context struct to keep Generator + // signatures modification easy. + struct Context { + mlir::Location loc; + mlir::OpBuilder *builder{nullptr}; + llvm::StringRef name; + llvm::ArrayRef arguments; + mlir::FunctionType funcType; + }; + + /// Define the different FIR generators that can be mapped to intrinsic to + /// generate the related code. + using Generator = mlir::Value *(Implementation::*)(Context &)const; + /// Search a runtime function that is associated to the generic intrinsic name + /// and whose signature matches the intrinsic arguments and result types. + /// If no such runtime function is found but a runtime function associated + /// with the Fortran generic exists and has the same number of arguments, + /// conversions will be inserted before and/or after the call. This is to + /// mainly to allow 16 bits float support even-though little or no math + /// runtime is currently available for it. + mlir::Value *generateRuntimeCall(Context &) const; + /// All generators can be combined with generateWrapperCall that will build a + /// function named "fir."+ + "." + and + /// generate the intrinsic implementation inside instead of at the intrinsic + /// call sites. This can be used to keep the FIR more readable. + template mlir::Value *generateWrapperCall(Context &c) const { + return outlineInWrapper(g, c); + } + /// The defaultGenerator is always attempted if no mapping was found for the + /// generic name provided. + mlir::Value *defaultGenerator(Context &c) const { + return generateWrapperCall<&I::generateRuntimeCall>(c); + } + + struct IntrinsicHanlder { + const char *name; + Generator generator{&I::defaultGenerator}; + }; + using I = Implementation; + /// Table that drives the fir generation depending on the intrinsic. + /// one to one mapping with Fortran arguments. If no mapping is + /// defined here for a generic intrinsic, the defaultGenerator will + /// be attempted. + static constexpr IntrinsicHanlder handlers[]{ + {"abs", &I::defaultGenerator}, + {"acos", &I::defaultGenerator}, + {"atan", &I::defaultGenerator}, + {"sqrt", &I::defaultGenerator}, + {"cos", &I::defaultGenerator}, + {"sin", &I::defaultGenerator}, + }; + + // helpers + static std::string getWrapperName(Context &); + mlir::Value *outlineInWrapper(Generator, Context &) const; + static mlir::FunctionType getFunctionType(mlir::Type resultType, + llvm::ArrayRef arguments, mlir::OpBuilder &); + + MathRuntimeLibrary runtime; }; -// TODO -// This table should be generated in a clever ways and probably shared with -// lib/evaluate intrinsic folding. -static const StaticDescription llvm[] = { - {"abs", "llvm.fabs.f32", Type::f32, {Type::f32}}, - {"abs", "llvm.fabs.f64", Type::f64, {Type::f64}}, - {"acos", "acosf", Type::f32, {Type::f32}}, - {"acos", "acos", Type::f64, {Type::f64}}, - {"atan", "atan2f", Type::f32, {Type::f32, Type::f32}}, - {"atan", "atan2", Type::f64, {Type::f64, Type::f64}}, - {"sqrt", "llvm.sqrt.f32", Type::f32, {Type::f32}}, - {"sqrt", "llvm.sqrt.f64", Type::f64, {Type::f64}}, - {"cos", "llvm.cos.f32", Type::f32, {Type::f32}}, - {"cos", "llvm.cos.f64", Type::f64, {Type::f64}}, - {"sin", "llvm.sin.f32", Type::f32, {Type::f32}}, - {"sin", "llvm.sin.f64", Type::f64, {Type::f64}}, +/// Define a simple static runtime description that will be transformed into +/// RuntimeFunction when building the IntrinsicLibrary. +/// It is constexpr constructible so that static tables of such descriptions can +/// be safely stored as global variables without requiring global constructors. +class RuntimeStaticDescription { +public: + /// Define possible runtime function argument/return type used in signature + /// descriptions. They follow mlir standard types naming. MLIR types cannot + /// directly be used because they can only be dynamically built. + enum class TypeCode { f32, f64 }; + /// C++ does not provide variable size constexpr container yet. TypeVector + /// implements one for Type elements. It works because Type is an enumeration. + struct TypeCodeVector { + template struct Storage { + static constexpr TypeCode values[]{v...}; + }; + template static constexpr TypeCodeVector create() { + const TypeCode *start{&Storage::values[0]}; + return TypeCodeVector{start, start + sizeof...(v)}; + } + const TypeCode *start{nullptr}; + const TypeCode *end{nullptr}; + }; + constexpr RuntimeStaticDescription( + const char *n, const char *s, TypeCode r, TypeCodeVector a) + : name{n}, symbol{s}, resultTypeCode{r}, argumentTypeCodes{a} {} + llvm::StringRef getSymbol() const { return symbol; } + llvm::StringRef getName() const { return name; } + /// Conversion between types of the static representation and MLIR types. + mlir::FunctionType getMLIRFunctionType(mlir::MLIRContext &) const; + +private: + static mlir::Type getMLIRType(TypeCode, mlir::MLIRContext &); + + const char *name{nullptr}; + const char *symbol{nullptr}; + TypeCode resultTypeCode; + TypeCodeVector argumentTypeCodes; }; -// Conversion between types of the static representation and MLIR types. -mlir::Type toMLIRType(Type t, mlir::MLIRContext &context) { - switch (t) { - case Type::f32: return mlir::FloatType::getF32(&context); - case Type::f64: return mlir::FloatType::getF64(&context); +/// Description of the runtime functions available on the target. +using RType = typename RuntimeStaticDescription::TypeCode; +using Args = typename RuntimeStaticDescription::TypeCodeVector; +static constexpr RuntimeStaticDescription llvmRuntime[] = { + {"abs", "llvm.fabs.f32", RType::f32, Args::create()}, + {"abs", "llvm.fabs.f64", RType::f64, Args::create()}, + {"acos", "acosf", RType::f32, Args::create()}, + {"acos", "acos", RType::f64, Args::create()}, + {"atan", "atan2f", RType::f32, Args::create()}, + {"atan", "atan2", RType::f64, Args::create()}, + {"sqrt", "llvm.sqrt.f32", RType::f32, Args::create()}, + {"sqrt", "llvm.sqrt.f64", RType::f64, Args::create()}, + {"cos", "llvm.cos.f32", RType::f32, Args::create()}, + {"cos", "llvm.cos.f64", RType::f64, Args::create()}, + {"sin", "llvm.sin.f32", RType::f32, Args::create()}, + {"sin", "llvm.sin.f64", RType::f64, Args::create()}, +}; +// TODO : This table should be generated in a clever ways and probably shared +// with lib/evaluate intrinsic folding. + +// Implementations + +// IntrinsicLibrary implementation + +IntrinsicLibrary IntrinsicLibrary::create( + IntrinsicLibrary::Version v, mlir::MLIRContext &context) { + IntrinsicLibrary lib{}; + lib.impl = new Implementation(v, context); + return lib; +} + +IntrinsicLibrary::~IntrinsicLibrary() { delete impl; } + +mlir::Value *IntrinsicLibrary::genval(mlir::Location loc, + mlir::OpBuilder &builder, llvm::StringRef name, mlir::Type resultType, + llvm::ArrayRef args) const { + assert(impl); + return impl->genval(loc, builder, name, resultType, args); +} + +// MathRuntimeLibrary implementation + +// Create the runtime description for the targeted library version. +// So far ignore the version an only load the dummy llvm lib. +MathRuntimeLibrary::MathRuntimeLibrary( + IntrinsicLibrary::Version, mlir::MLIRContext &context) { + for (const RuntimeStaticDescription &func : llvmRuntime) { + Key key{func.getName()}; + RuntimeFunction impl{func.getSymbol(), func.getMLIRFunctionType(context)}; + library.insert({key, impl}); } } -mlir::FunctionType toMLIRFunctionType( - const StaticDescription &func, mlir::MLIRContext &context) { - std::vector argMLIRTypes; - for (runtime::Type t : func.argumentTypes) { - argMLIRTypes.push_back(toMLIRType(t, context)); + +mlir::FuncOp MathRuntimeLibrary::getFuncOp( + mlir::OpBuilder &builder, const RuntimeFunction &runtime) const { + auto module{getModule(&builder)}; + mlir::FuncOp function{getNamedFunction(module, runtime.symbol)}; + if (!function) { + function = createFunction(module, runtime.symbol, runtime.type); + function.setAttr("fir.runtime", builder.getUnitAttr()); + } + return function; +} + +llvm::Optional MathRuntimeLibrary::getFunction( + mlir::OpBuilder &builder, llvm::StringRef name, + mlir::FunctionType funcType) const { + auto range{library.equal_range(name)}; + const RuntimeFunction *bestNearMatch{nullptr}; + for (auto iter{range.first}; iter != range.second; ++iter) { + const RuntimeFunction &impl{iter->second}; + if (funcType == impl.type) { + return getFuncOp(builder, impl); // exact match + } else { + if (!bestNearMatch && + impl.type.getNumResults() == funcType.getNumResults() && + impl.type.getNumInputs() == funcType.getNumInputs()) { + bestNearMatch = &impl; + } else { + // TODO the best near match should not be the first hit. + // It should apply rules: + // -> Non narrowing argument conversion are better + // -> The "nearest" conversions are better + } + } + } + if (bestNearMatch != nullptr) { + return getFuncOp(builder, *bestNearMatch); + } else { + return {}; + } +} + +// IntrinsicLibrary::Implementation implementation + +mlir::Value *IntrinsicLibrary::Implementation::genval(mlir::Location loc, + mlir::OpBuilder &builder, llvm::StringRef name, mlir::Type resultType, + llvm::ArrayRef args) const { + Context context{ + loc, &builder, name, args, getFunctionType(resultType, args, builder)}; + for (const IntrinsicHanlder &handler : handlers) { + if (name == handler.name) { + assert(handler.generator != nullptr); + return (this->*handler.generator)(context); + } + } + // Try the default generator if no special handler was defined for the + // intrinsic being called. + return this->defaultGenerator(context); +} + +mlir::FunctionType IntrinsicLibrary::Implementation::getFunctionType( + mlir::Type resultType, llvm::ArrayRef arguments, + mlir::OpBuilder &builder) { + llvm::SmallVector argumentTypes; + for (const auto &arg : arguments) { + assert(arg != nullptr); // TODO think about optionals + argumentTypes.push_back(arg->getType()); } return mlir::FunctionType::get( - argMLIRTypes, toMLIRType(func.resultType, context), &context); + argumentTypes, resultType, getModule(&builder).getContext()); } -} // runtime -std::optional IntrinsicLibrary::getFunction( - llvm::StringRef name, mlir::Type type, mlir::OpBuilder &builder) const { - auto module{getModule(&builder)}; - if (const auto &it{lib.find({name, type})}; it != lib.end()) { - const IntrinsicImplementation &impl{it->second}; - if (mlir::FuncOp func{getNamedFunction(module, impl.symbol)}) { - return func; +std::string IntrinsicLibrary::Implementation::getWrapperName(Context &c) { + // TODO find nicer type to string infra + llvm::StringRef typeName{"unknown"}; + assert(c.funcType.getNumResults() == 1); + mlir::Type resultType{c.funcType.getResult(0)}; + if (resultType.isF16()) { + typeName = "f16"; + } else if (resultType.isF32()) { + typeName = "f32"; + } else if (resultType.isF64()) { + typeName = "f64"; + } else { + assert(false); + } + return "fir." + c.name.str() + "." + typeName.str(); +} + +mlir::Value *IntrinsicLibrary::Implementation::outlineInWrapper( + Generator generator, Context &context) const { + mlir::ModuleOp module{getModule(context.builder)}; + mlir::MLIRContext *mlirContext{module.getContext()}; + std::string wrapperName{getWrapperName(context)}; + mlir::FuncOp function{getNamedFunction(module, wrapperName)}; + if (!function) { + // First time this wrapper is needed, build it. + function = createFunction(module, wrapperName, context.funcType); + function.setAttr("fir.intrinsic", context.builder->getUnitAttr()); + function.addEntryBlock(); + + // Create local context to emit code into the newly created function + // This new function is not linked to a source file location, only + // its calls will be. + Context localContext{context}; + auto localBuilder{std::make_unique(function)}; + localBuilder->setInsertionPointToStart(&function.front()); + localContext.builder = &(*localBuilder); + llvm::SmallVector localArguments; + for (mlir::BlockArgument *bArg : function.front().getArguments()) { + localArguments.push_back(bArg); } - mlir::FuncOp function{createFunction(module, impl.symbol, impl.type)}; - function.setAttr("fir.intrinsic", builder.getUnitAttr()); - return function; + localContext.arguments = localArguments; + localContext.loc = mlir::UnknownLoc::get(mlirContext); + + mlir::Value *result{(this->*generator)(localContext)}; + localBuilder->create(localContext.loc, result); } else { - return std::nullopt; + // Wrapper was already built, ensure it has the sought type + assert(function.getType() == context.funcType); } + auto call{context.builder->create( + context.loc, function, context.arguments)}; + return call.getResult(0); } -// So far ignore the version an only load the dummy llvm lib. -IntrinsicLibrary IntrinsicLibrary::create( - IntrinsicLibrary::Version, mlir::MLIRContext &context) { - Map map; - for (const auto &func : runtime::llvm) { - IntrinsicLibrary::Key key{ - func.name, runtime::toMLIRType(func.resultType, context)}; - IntrinsicImplementation impl{ - func.symbol, runtime::toMLIRFunctionType(func, context)}; - map.insert({key, impl}); +mlir::Value *IntrinsicLibrary::Implementation::generateRuntimeCall( + Context &context) const { + // Look up runtime + mlir::FunctionType soughtFuncType{context.funcType}; + if (auto funcOp{runtime.getFunction( + *context.builder, context.name, soughtFuncType)}) { + mlir::FunctionType actualFuncType{funcOp->getType()}; + if (actualFuncType.getNumResults() != soughtFuncType.getNumResults() || + actualFuncType.getNumInputs() != soughtFuncType.getNumInputs() || + actualFuncType.getNumInputs() != context.arguments.size() || + actualFuncType.getNumResults() != 1) { + assert(false); // TODO better error handling + return nullptr; + } + llvm::SmallVector convertedArguments; + int i{0}; + for (mlir::Value *arg : context.arguments) { + mlir::Type actualType{actualFuncType.getInput(i)}; + if (soughtFuncType.getInput(i) != actualType) { + auto castedArg{context.builder->create( + context.loc, actualType, arg)}; + convertedArguments.push_back(castedArg.getResult()); + } else { + convertedArguments.push_back(arg); + } + ++i; + } + auto call{context.builder->create( + context.loc, *funcOp, convertedArguments)}; + mlir::Type soughtType{soughtFuncType.getResult(0)}; + mlir::Value *res{call.getResult(0)}; + if (actualFuncType.getResult(0) != soughtType) { + auto castedRes{context.builder->create( + context.loc, soughtType, res)}; + return castedRes.getResult(); + } else { + return res; + } + } else { + // could not find runtime function + assert(false); // TODO: better error handling. + return nullptr; } - return IntrinsicLibrary{std::move(map)}; } + +// RuntimeStaticDescription implementation + +mlir::Type RuntimeStaticDescription::getMLIRType( + TypeCode t, mlir::MLIRContext &context) { + switch (t) { + case TypeCode::f32: return mlir::FloatType::getF32(&context); + case TypeCode::f64: return mlir::FloatType::getF64(&context); + } +} + +mlir::FunctionType RuntimeStaticDescription::getMLIRFunctionType( + mlir::MLIRContext &context) const { + llvm::SmallVector argMLIRTypes; + for (const TypeCode *t{argumentTypeCodes.start}; + t != nullptr && t != argumentTypeCodes.end; ++t) { + argMLIRTypes.push_back(getMLIRType(*t, context)); + } + mlir::Type resMLIRType{getMLIRType(resultTypeCode, context)}; + return mlir::FunctionType::get(argMLIRTypes, resMLIRType, &context); +} + } diff --git a/lib/burnside/intrinsics.h b/lib/burnside/intrinsics.h index fec48b579450..d2534b71d1e3 100644 --- a/lib/burnside/intrinsics.h +++ b/lib/burnside/intrinsics.h @@ -18,53 +18,55 @@ #include "llvm/ADT/StringRef.h" #include "mlir/Dialect/StandardOps/Ops.h" #include -#include -#include /// [Coding style](https://llvm.org/docs/CodingStandards.html) namespace Fortran::burnside { -/// IntrinsicLibrary holds the runtime description of intrinsics. It aims -/// at abstracting which library version is used to implement Fortran -/// numerical intrinsics while lowering expressions. -/// It can be probed for a certain intrinsic and will return an mlir::FuncOp -/// that matches the targeted library implementation. +/// IntrinsicLibrary generates FIR+MLIR operations that implement Fortran +/// generic intrinsic function calls. It operates purely on FIR+MLIR types so +/// that it can be used at different lowering level if needed. +/// IntrinsicLibrary is not in charge of generating code for the argument +/// expressions/symbols. These must be generated before and the resulting +/// mlir::Values* are inputs for the IntrinsicLibrary operation generation. +/// +/// The operations generated can be as simple as a single runtime library call +/// or they may fully implement the intrinsic without runtime help. This +/// depends on the IntrinsicLibrary::Implementation. +/// +/// IntrinsicLibrary should not be assumed cheap to build since they may need +/// to build a representation of the target runtime before they can be used. +/// Once built, they are stateless and cannot be modified. +/// + class IntrinsicLibrary { public: - /// Available intrinsic library versions. + /// Available runtime library versions. enum class Version { PgmathFast, PgmathRelaxed, PgmathPrecise, LLVM }; - using Key = std::pair; - // Need a custom hash for this kind of keys. LLVM provides it. - struct Hash { - size_t operator()(const Key &k) const { return llvm::hash_value(k); } - }; - /// Internal structure to describe the runtime function. An intrinsic function - /// is not declared in mlir until the IntrinsicLibrary needs to return it. - /// This is to avoid polluting the LLVM IR with useless declarations. - /// This structure allows generating mlir::FuncOp on the fly. - struct IntrinsicImplementation { - IntrinsicImplementation(llvm::StringRef n, mlir::FunctionType t) - : symbol{n}, type{t} {}; - llvm::StringRef symbol; - mlir::FunctionType type; - }; - using Map = std::unordered_map; - /// Probe the intrinsic library for a certain intrinsic and get/build the - /// related mlir::FuncOp if a runtime description is found. - /// Also add an unit attribute "fir.intrinsic" to the function so that later - /// it is possible to quickly know what function are intrinsics vs users. - std::optional getFunction( - llvm::StringRef, mlir::Type, mlir::OpBuilder &) const; + ~IntrinsicLibrary(); + /// Generate the FIR+MLIR operations for the generic intrinsic "name". + /// On failure, returns a nullptr, else the returned mlir::Value* is + /// the returned Fortran intrinsic value. + mlir::Value *genval(mlir::Location loc, mlir::OpBuilder &builder, + llvm::StringRef name, mlir::Type resultType, + llvm::ArrayRef args) const; + + // TODO: Expose interface to get specific intrinsic function address. + // TODO: Handle intrinsic subroutine. + // TODO: Intrinsics that do not require their arguments to be defined + // (e.g shape inquiries) might not fit in the current interface that + // requires mlir::Value* to be provided. + // TODO: Error handling interface ? + // TODO: Implementation is incomplete. Many intrinsics to tbd. - /// Create the runtime description for the targeted library version. + /// Create an IntrinsicLibrary targeting the desired runtime library version. static IntrinsicLibrary create(Version, mlir::MLIRContext &); private: - IntrinsicLibrary(Map &&l) : lib{std::move(l)} {}; - /// Holds the intrinsic runtime description to be probed. - Map lib; + /// Actual implementation is hidden. + class Implementation; + Implementation *impl{nullptr}; // owning pointer }; } From 1e4b5fc05bcb62aabfa694c53abe5d867664b046 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 15 Oct 2019 09:08:29 -0700 Subject: [PATCH 015/123] add more interface to allow source positions to be tracked for functions identified structs; box ops; add complex changes from 8c33745 --- include/fir/FIROps.td | 18 +- include/fir/Type.h | 29 +-- lib/burnside/builder.cc | 9 +- lib/burnside/builder.h | 19 +- lib/burnside/convert-expr.cc | 76 ++++-- lib/burnside/fe-helper.cc | 10 +- lib/burnside/fe-helper.h | 8 +- lib/burnside/intrinsics.cc | 109 +++++++-- lib/fir/FIROps.cpp | 24 +- lib/fir/StdConverter.cpp | 6 +- lib/fir/Tilikum.cpp | 431 +++++++++++++++++++++++++---------- lib/fir/Type.cpp | 23 +- 12 files changed, 544 insertions(+), 218 deletions(-) diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index c51e5f43c94e..b84a8a572d51 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -57,7 +57,8 @@ def fir_SequenceType : Type()">, "array type">; def AnyCompositeLike : TypeConstraint, "any composite">; + fir_SequenceType.predicate, fir_ComplexType.predicate, + IsTupleTypePred]>, "any composite">; def fir_ReferenceType : Type()">, "reference type">; @@ -1474,7 +1475,7 @@ def fir_WhereOp : fir_Op<"where", [ImplicitFirTerminator]> { def fir_CallOp : fir_Op<"call", []>, Results<(outs Variadic)>, - Arguments<(ins SymbolRefAttr:$proc, Variadic:$args)> { + Arguments<(ins SymbolRefAttr:$callee, Variadic:$args)> { let summary = "call a procedure directly"; let description = [{ @@ -1486,7 +1487,7 @@ def fir_CallOp : fir_Op<"call", []>, M::FunctionType calleeType; M::SymbolRefAttr proc; L::SmallVector operands; - if (parser.parseAttribute(proc, "proc", result.attributes) || + if (parser.parseAttribute(proc, "callee", result.attributes) || parser.parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || parser.parseOptionalAttributeDict(result.attributes) || parser.parseColonType(calleeType) || @@ -1497,10 +1498,10 @@ def fir_CallOp : fir_Op<"call", []>, return M::success(); }]; let printer = [{ - p << getOperationName() << ' ' << getAttr("proc") << '('; + p << getOperationName() << ' ' << getAttr("callee") << '('; p.printOperands(args()); p << ')'; - p.printOptionalAttrDict(getAttrs(), {"proc"}); + p.printOptionalAttrDict(getAttrs(), {"callee"}); p << " : "; p.printFunctionalType(getOperation()); }]; @@ -1543,10 +1544,11 @@ def fir_ICallOp : fir_Op<"icall", []>, p << '('; p.printOperands(args()); p << ')'; - p.printOptionalAttrDict(getAttrs(), {"proc"}); + p.printOptionalAttrDict(getAttrs(), {}); p << " : " << getCallee()->getType(); }]; let extraClassDeclaration = [{ + mlir::FunctionType getFunctionType(); Value *getCallee() { return getOperand(0); } operand_range getArgOperands() { @@ -1588,6 +1590,7 @@ def fir_DispatchOp : fir_Op<"dispatch", []>, parser.resolveOperands( operands, calleeType.getInputs(), calleeLoc, result.operands)) return M::failure(); + result.addAttribute("fn_type", parser.getBuilder().getTypeAttr(calleeType)); return M::success(); }]; @@ -1599,13 +1602,14 @@ def fir_DispatchOp : fir_Op<"dispatch", []>, p.printOperands(args()); } p << ')'; - p.printOptionalAttrDict(getAttrs(), {"method"}); + p.printOptionalAttrDict(getAttrs(), {"fn_type", "method"}); llvm::SmallVector resTy(getResultTypes()); llvm::SmallVector argTy(getOperandTypes()); p << " : " << M::FunctionType::get(argTy, resTy, getContext()); }]; let extraClassDeclaration = [{ + mlir::FunctionType getFunctionType(); operand_range getArgOperands() { return {arg_operand_begin(), arg_operand_end()}; } diff --git a/include/fir/Type.h b/include/fir/Type.h index 5e4836abf34f..8a469c4d35b8 100644 --- a/include/fir/Type.h +++ b/include/fir/Type.h @@ -90,8 +90,16 @@ class CharacterType public: using Base::Base; static CharacterType get(mlir::MLIRContext *ctxt, KindTy kind); - int getSizeInBits() const; - KindTy getFKind() const { return getSizeInBits() / 8; } + 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 @@ -100,8 +108,7 @@ class IntType public: using Base::Base; static IntType get(mlir::MLIRContext *ctxt, KindTy kind); - int getSizeInBits() const; - KindTy getFKind() const { return getSizeInBits() / 8; } + KindTy getFKind() const; }; class LogicalType @@ -111,8 +118,7 @@ class LogicalType public: using Base::Base; static LogicalType get(mlir::MLIRContext *ctxt, KindTy kind); - int getSizeInBits() const; - KindTy getFKind() const { return getSizeInBits() / 8; } + KindTy getFKind() const; }; class RealType : public mlir::Type::TypeBase, - public IntrinsicTypeMixin { -public: - using Base::Base; - static CplxType get(mlir::MLIRContext *ctxt, KindTy kind); - int getSizeInBits() const; KindTy getFKind() const; }; diff --git a/lib/burnside/builder.cc b/lib/burnside/builder.cc index c626525f53f4..650f80c21790 100644 --- a/lib/burnside/builder.cc +++ b/lib/burnside/builder.cc @@ -30,10 +30,13 @@ std::string B::applyNameMangling(llvm::StringRef parserName) { return "_Qp_"s + parserName.str(); } -mlir::FuncOp B::createFunction( - mlir::ModuleOp module, llvm::StringRef name, mlir::FunctionType funcTy) { +mlir::FuncOp B::createFunction(mlir::ModuleOp module, llvm::StringRef name, + mlir::FunctionType funcTy, parser::CookedSource const *cooked, + parser::CharBlock const *cb) { mlir::MLIRContext *ctxt{module.getContext()}; - auto func{mlir::FuncOp::create(dummyLoc(ctxt), name, funcTy)}; + mlir::Location loc{ + (cooked && cb) ? parserPosToLoc(*ctxt, cooked, *cb) : dummyLoc(*ctxt)}; + auto func{mlir::FuncOp::create(loc, name, funcTy)}; module.push_back(func); return func; } diff --git a/lib/burnside/builder.h b/lib/burnside/builder.h index 0d0c9c11086e..a2bce6b79faa 100644 --- a/lib/burnside/builder.h +++ b/lib/burnside/builder.h @@ -26,7 +26,12 @@ namespace llvm { class StringRef; } -namespace Fortran::burnside { +namespace Fortran { +namespace parser { +class CookedSource; +} + +namespace burnside { /// Miscellaneous helper routines for building MLIR /// [Coding style](https://llvm.org/docs/CodingStandards.html) @@ -70,9 +75,13 @@ inline mlir::Block *createBlock(mlir::OpBuilder *bldr) { mlir::FuncOp getNamedFunction(mlir::ModuleOp, llvm::StringRef name); /// Create a new Function -mlir::FuncOp createFunction( - mlir::ModuleOp module, llvm::StringRef name, mlir::FunctionType funcTy); - -} // Fortran::burnside +/// Both the `CookedSource` and `CharBlock` position should be provided to +/// properly track source position information. +mlir::FuncOp createFunction(mlir::ModuleOp module, llvm::StringRef name, + mlir::FunctionType funcTy, parser::CookedSource const *cooked = nullptr, + parser::CharBlock const *cb = nullptr); + +} // burnside +} // Fortran #endif // FORTRAN_BURNSIDE_BUILDER_H_ diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index bdbe9f0105ed..9b7e8d7161b4 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -239,8 +239,15 @@ class ExprLowering { template M::Value *genval(Ev::TypeParamInquiry const &) { TODO(); } - template M::Value *genval(Ev::ComplexComponent const &) { - TODO(); + template M::Value *genval(Ev::ComplexComponent const &part) { + auto *ctxt = builder.getContext(); + auto realTy{getFIRType(ctxt, defaults, RealCat, KIND)}; + auto index = genIntegerConstant<4>(ctxt, part.isImaginaryPart ? 1 : 0); + L::SmallVector input({fir::CplxType::get(ctxt, KIND)}); + L::SmallVector output({realTy}); + auto funcTy = M::FunctionType::get(input, output, ctxt); + return builder.create( + getLoc(), funcTy, genval(part.left()), index); } template M::Value *genval(Ev::Negate> const &) { @@ -253,7 +260,8 @@ class ExprLowering { } else if constexpr (TC == RealCat) { return createBinaryOp(op); } else { - TODO(); + static_assert(TC == ComplexCat, "Expected numeric type"); + return createBinaryOp(op); } } template @@ -263,7 +271,8 @@ class ExprLowering { } else if constexpr (TC == RealCat) { return createBinaryOp(op); } else { - TODO(); + static_assert(TC == ComplexCat, "Expected numeric type"); + return createBinaryOp(op); } } template @@ -273,7 +282,8 @@ class ExprLowering { } else if constexpr (TC == RealCat) { return createBinaryOp(op); } else { - TODO(); + static_assert(TC == ComplexCat, "Expected numeric type"); + return createBinaryOp(op); } } template @@ -282,28 +292,45 @@ class ExprLowering { return createBinaryOp(op); } else if constexpr (TC == RealCat) { return createBinaryOp(op); - } else if constexpr (TC == ComplexCat) { - return createBinaryFIRTCall(op, FIRT_CDIV); } else { - TODO(); + static_assert(TC == ComplexCat, "Expected numeric type"); + return createBinaryOp(op); } } template M::Value *genval(Ev::Power> const &op) { - if constexpr (TC == IntegerCat) { - return createBinaryFIRTCall(op, FIRT_POW); - } else { - TODO(); - } + llvm::SmallVector operands{ + genval(op.left()), genval(op.right())}; + M::Type ty{getFIRType(builder.getContext(), defaults, TC, KIND)}; + return intrinsics.genval(getLoc(), builder, "pow", ty, operands); } template - M::Value *genval(Ev::RealToIntPower> const &) { - TODO(); - } - template M::Value *genval(Ev::ComplexConstructor const &) { - TODO(); + M::Value *genval(Ev::RealToIntPower> const &op) { + // TODO: runtime as limited integer kind support. Look if the conversions + // are ok + llvm::SmallVector operands{ + genval(op.left()), genval(op.right())}; + M::Type ty{getFIRType(builder.getContext(), defaults, TC, KIND)}; + return intrinsics.genval(getLoc(), builder, "pow", ty, operands); + } + template M::Value *genval(Ev::ComplexConstructor const &op) { + auto *ctxt = builder.getContext(); + auto complexTy{fir::CplxType::get(ctxt, KIND)}; + auto loc{getLoc()}; + L::SmallVector input( + {getFIRType(ctxt, defaults, RealCat, KIND)}); + L::SmallVector output({complexTy}); + auto fTy = M::FunctionType::get(input, output, builder.getContext()); + auto und = builder.create(loc, complexTy); + auto *lf = genval(op.left()); + auto *realIdx = genIntegerConstant<4>(ctxt, 0); + auto rp = builder.create(loc, fTy, und, lf, realIdx); + auto *rt = genval(op.right()); + auto *imagIdx = genIntegerConstant<4>(ctxt, 1); + return builder.create(loc, fTy, rp, rt, imagIdx); } template M::Value *genval(Ev::Concat const &op) { + // TODO this is a bogus call return createBinaryFIRTCall(op, FIRT_CONCAT); } @@ -360,6 +387,10 @@ class ExprLowering { template M::Value *genval(Ev::Constant> const &con) { + // TODO: + // - character type constant + // - array constant not handled + // - derived type constant if constexpr (TC == IntegerCat) { auto opt{con.GetScalarValue()}; if (opt.has_value()) @@ -396,6 +427,15 @@ class ExprLowering { } assert(false && "real constant has no value"); return {}; + } else if constexpr (TC == ComplexCat) { + auto opt{con.GetScalarValue()}; + if (opt.has_value()) { + using TR = Ev::Type; + return genval(Ev::ComplexConstructor{ + Ev::Expr{Ev::Constant{opt->REAL()}}, + Ev::Expr{Ev::Constant{opt->AIMAG()}}}); + } + assert(false && "array of complex unhandled"); } else { assert(false && "unhandled constant"); return {}; diff --git a/lib/burnside/fe-helper.cc b/lib/burnside/fe-helper.cc index 5c9c0aad23f5..72265d6b66fe 100644 --- a/lib/burnside/fe-helper.cc +++ b/lib/burnside/fe-helper.cc @@ -332,24 +332,22 @@ class TypeBuilder { } // namespace -/// Generate an unknown location -M::Location Br::dummyLoc(M::MLIRContext *ctxt) { - return M::UnknownLoc::get(ctxt); +M::Location Br::dummyLoc(M::MLIRContext &context) { + return M::UnknownLoc::get(&context); } -// What do we need to convert a CharBlock to actual source locations? -// FIXME: replace with a map from a provenance to a source location M::Location Br::parserPosToLoc(M::MLIRContext &context, const Pa::CookedSource *cooked, const Pa::CharBlock &position) { if (cooked) { auto loc{cooked->GetSourcePositionRange(position)}; if (loc.has_value()) { + // loc is a pair (begin, end); use the beginning position auto &filePos{loc->first}; return M::FileLineColLoc::get( filePos.file.path(), filePos.line, filePos.column, &context); } } - return M::UnknownLoc::get(&context); + return dummyLoc(context); } M::Type Br::getFIRType(M::MLIRContext *context, diff --git a/lib/burnside/fe-helper.h b/lib/burnside/fe-helper.h index 6eebd673280f..3eb90d83d9c2 100644 --- a/lib/burnside/fe-helper.h +++ b/lib/burnside/fe-helper.h @@ -63,11 +63,13 @@ constexpr common::TypeCategory CharacterCat{common::TypeCategory::Character}; constexpr common::TypeCategory LogicalCat{common::TypeCategory::Logical}; constexpr common::TypeCategory DerivedCat{common::TypeCategory::Derived}; -mlir::Location dummyLoc(mlir::MLIRContext *ctxt); +/// Generate a dummy location when there is no origin +mlir::Location dummyLoc(mlir::MLIRContext &context); -/// Translate a CharBlock position to (source-file, line, column) +/// Convert a `CharBlock` front-end position pointer into the `(file, line, +/// column)` triple for use in MLIR, LLVM, and ultimately DWARF. mlir::Location parserPosToLoc(mlir::MLIRContext &context, - const parser::CookedSource *cooked, const parser::CharBlock &position); + parser::CookedSource const *cooked, parser::CharBlock const &position); mlir::Type getFIRType(mlir::MLIRContext *ctxt, common::IntrinsicTypeDefaultKinds const &defaults, common::TypeCategory tc, diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index e49732cd4ba3..ec351b710b55 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -14,8 +14,11 @@ #include "intrinsics.h" #include "builder.h" +#include "fe-helper.h" #include "fir/FIROps.h" +#include "fir/Type.h" #include "llvm/ADT/Optional.h" +#include #include #include @@ -97,11 +100,18 @@ class IntrinsicLibrary::Implementation { llvm::StringRef name; llvm::ArrayRef arguments; mlir::FunctionType funcType; + mlir::MLIRContext *getMLIRContext() { + return getModule(builder).getContext(); + } + mlir::Type getResultType() { + assert(funcType.getNumResults() == 1); + return funcType.getResult(0); + } }; /// Define the different FIR generators that can be mapped to intrinsic to /// generate the related code. - using Generator = mlir::Value *(Implementation::*)(Context &)const; + using Generator = mlir::Value *(Implementation::*)(Context &) const; /// Search a runtime function that is associated to the generic intrinsic name /// and whose signature matches the intrinsic arguments and result types. /// If no such runtime function is found but a runtime function associated @@ -122,6 +132,7 @@ class IntrinsicLibrary::Implementation { mlir::Value *defaultGenerator(Context &c) const { return generateWrapperCall<&I::generateRuntimeCall>(c); } + mlir::Value *conjgGenerator(Context &) const; struct IntrinsicHanlder { const char *name; @@ -133,12 +144,7 @@ class IntrinsicLibrary::Implementation { /// defined here for a generic intrinsic, the defaultGenerator will /// be attempted. static constexpr IntrinsicHanlder handlers[]{ - {"abs", &I::defaultGenerator}, - {"acos", &I::defaultGenerator}, - {"atan", &I::defaultGenerator}, - {"sqrt", &I::defaultGenerator}, - {"cos", &I::defaultGenerator}, - {"sin", &I::defaultGenerator}, + {"conjg", &I::conjgGenerator}, }; // helpers @@ -159,7 +165,7 @@ class RuntimeStaticDescription { /// Define possible runtime function argument/return type used in signature /// descriptions. They follow mlir standard types naming. MLIR types cannot /// directly be used because they can only be dynamically built. - enum class TypeCode { f32, f64 }; + enum class TypeCode { i32, i64, f32, f64, c32, c64 }; /// C++ does not provide variable size constexpr container yet. TypeVector /// implements one for Type elements. It works because Type is an enumeration. struct TypeCodeVector { @@ -207,7 +213,31 @@ static constexpr RuntimeStaticDescription llvmRuntime[] = { {"sin", "llvm.sin.f32", RType::f32, Args::create()}, {"sin", "llvm.sin.f64", RType::f64, Args::create()}, }; -// TODO : This table should be generated in a clever ways and probably shared + +static constexpr RuntimeStaticDescription pgmathPreciseRuntime[] = { + {"acos", "__pc_acos_1", RType::c32, Args::create()}, + {"acos", "__pz_acos_1", RType::c64, Args::create()}, + {"pow", "__pc_pow_1", RType::c32, Args::create()}, + {"pow", "__pc_powi_1", RType::c32, Args::create()}, + {"pow", "__pc_powk_1", RType::c32, Args::create()}, + {"pow", "__pd_pow_1", RType::f64, Args::create()}, + {"pow", "__pd_powi_1", RType::f64, Args::create()}, + {"pow", "__pd_powk_1", RType::f64, Args::create()}, + {"pow", "__ps_pow_1", RType::f32, Args::create()}, + {"pow", "__ps_powi_1", RType::f32, Args::create()}, + {"pow", "__ps_powk_1", RType::f32, Args::create()}, + {"pow", "__pz_pow_1", RType::c64, Args::create()}, + {"pow", "__pz_powi_1", RType::c64, Args::create()}, + {"pow", "__pz_powk_1", RType::c64, Args::create()}, + {"pow", "__mth_i_ipowi", RType::i32, + Args::create()}, + {"pow", "__mth_i_kpowi", RType::i64, + Args::create()}, + {"pow", "__mth_i_kpowk", RType::i64, + Args::create()}, +}; + +// TODO : Tables above should be generated in a clever ways and probably shared // with lib/evaluate intrinsic folding. // Implementations @@ -233,13 +263,16 @@ mlir::Value *IntrinsicLibrary::genval(mlir::Location loc, // MathRuntimeLibrary implementation // Create the runtime description for the targeted library version. -// So far ignore the version an only load the dummy llvm lib. +// So far ignore the version an only load the dummy llvm lib and pgmath precise MathRuntimeLibrary::MathRuntimeLibrary( IntrinsicLibrary::Version, mlir::MLIRContext &context) { for (const RuntimeStaticDescription &func : llvmRuntime) { - Key key{func.getName()}; RuntimeFunction impl{func.getSymbol(), func.getMLIRFunctionType(context)}; - library.insert({key, impl}); + library.insert({Key{func.getName()}, impl}); + } + for (const RuntimeStaticDescription &func : pgmathPreciseRuntime) { + RuntimeFunction impl{func.getSymbol(), func.getMLIRFunctionType(context)}; + library.insert({Key{func.getName()}, impl}); } } @@ -315,19 +348,20 @@ mlir::FunctionType IntrinsicLibrary::Implementation::getFunctionType( std::string IntrinsicLibrary::Implementation::getWrapperName(Context &c) { // TODO find nicer type to string infra - llvm::StringRef typeName{"unknown"}; + llvm::StringRef prefix{"fir."}; assert(c.funcType.getNumResults() == 1); mlir::Type resultType{c.funcType.getResult(0)}; - if (resultType.isF16()) { - typeName = "f16"; - } else if (resultType.isF32()) { - typeName = "f32"; - } else if (resultType.isF64()) { - typeName = "f64"; + if (auto f{resultType.dyn_cast()}) { + return prefix.str() + c.name.str() + ".f" + std::to_string(f.getWidth()); + } else if (auto i{resultType.dyn_cast()}) { + return prefix.str() + c.name.str() + ".i" + std::to_string(i.getWidth()); + } else if (auto cplx{resultType.dyn_cast()}) { + // TODO using kind here is weird, but I do not want to hard coded mapping + return prefix.str() + c.name.str() + ".c" + std::to_string(cplx.getFKind()); } else { assert(false); + return "fir." + c.name.str() + ".unknown"; } - return "fir." + c.name.str() + "." + typeName.str(); } mlir::Value *IntrinsicLibrary::Implementation::outlineInWrapper( @@ -412,14 +446,49 @@ mlir::Value *IntrinsicLibrary::Implementation::generateRuntimeCall( } } +// CONJG +mlir::Value *IntrinsicLibrary::Implementation::conjgGenerator( + Context &genCtxt) const { + assert(genCtxt.arguments.size() == 1); + mlir::Type resType{genCtxt.getResultType()}; + assert(resType == genCtxt.arguments[0]->getType()); + + // FIXME make fir.complex.. instead of fir.complex<4> to avoid + // constantly having to translate to fe types ? Or get actual mapping + auto complexType{resType.cast()}; + mlir::Type realType{ + burnside::convertReal(genCtxt.getMLIRContext(), complexType.getFKind())}; + auto ity{mlir::IndexType::get(genCtxt.getMLIRContext())}; + auto oneAttr{genCtxt.builder->getIntegerAttr(ity, 1)}; + auto *imagIdx{ + genCtxt.builder->create(genCtxt.loc, ity, oneAttr) + .getResult()}; + auto parts{genCtxt.builder->create( + genCtxt.loc, realType, genCtxt.arguments[0], imagIdx)}; + // TODO a negation unary would be better than a sub to zero ? + auto zeroAttr{genCtxt.builder->getFloatAttr(realType, llvm::APFloat{0.})}; + auto zero{genCtxt.builder->create( + genCtxt.loc, realType, zeroAttr)}; + auto negImag{genCtxt.builder->create(genCtxt.loc, zero, parts)}; + return genCtxt.builder->create( + genCtxt.loc, resType, genCtxt.arguments[0], negImag, imagIdx); +} + // RuntimeStaticDescription implementation mlir::Type RuntimeStaticDescription::getMLIRType( TypeCode t, mlir::MLIRContext &context) { switch (t) { + case TypeCode::i32: return mlir::IntegerType::get(32, &context); + case TypeCode::i64: return mlir::IntegerType::get(64, &context); case TypeCode::f32: return mlir::FloatType::getF32(&context); case TypeCode::f64: return mlir::FloatType::getF64(&context); + // TODO need to access mapping between fe/target + case TypeCode::c32: return fir::CplxType::get(&context, 4); + case TypeCode::c64: return fir::CplxType::get(&context, 8); } + assert(false && "bug"); + return {}; } mlir::FunctionType RuntimeStaticDescription::getMLIRFunctionType( diff --git a/lib/fir/FIROps.cpp b/lib/fir/FIROps.cpp index ead744b4300e..cded90c33290 100644 --- a/lib/fir/FIROps.cpp +++ b/lib/fir/FIROps.cpp @@ -42,6 +42,15 @@ M::Type AllocMemOp::getAllocatedType() { M::Type AllocMemOp::getRefTy(M::Type ty) { return HeapType::get(ty); } +// CallOp + +// DispatchOp + +M::FunctionType DispatchOp::getFunctionType() { + auto attr = getAttr("fn_type").cast(); + return attr.getValue().cast(); +} + // DispatchTableOp void DispatchTableOp::build(M::Builder *builder, M::OperationState *result, @@ -166,19 +175,22 @@ void GlobalOp::appendInitialValue(M::Operation *op) { M::Region &GlobalOp::front() { return this->getOperation()->getRegion(0); } +// ICallOp + +M::FunctionType ICallOp::getFunctionType() { + return getCallee()->getType().cast(); +} + // LoadOp /// Get the element type of a reference like type; otherwise null M::Type elementTypeOf(M::Type ref) { - if (auto r = ref.dyn_cast_or_null()) { + if (auto r = ref.dyn_cast_or_null()) return r.getEleTy(); - } - if (auto r = ref.dyn_cast_or_null()) { + if (auto r = ref.dyn_cast_or_null()) return r.getEleTy(); - } - if (auto r = ref.dyn_cast_or_null()) { + if (auto r = ref.dyn_cast_or_null()) return r.getEleTy(); - } return {}; } diff --git a/lib/fir/StdConverter.cpp b/lib/fir/StdConverter.cpp index b766630a9126..9736b8b94fc0 100644 --- a/lib/fir/StdConverter.cpp +++ b/lib/fir/StdConverter.cpp @@ -52,7 +52,7 @@ using AttributeTy = L::ArrayRef; /// This converts a subset of FIR types to standard types class FIRToStdTypeConverter : public M::TypeConverter { public: - explicit FIRToStdTypeConverter() {} + using TypeConverter::TypeConverter; // convert a front-end kind value to either a std dialect type static M::Type kindToRealType(M::MLIRContext *ctx, KindTy kind) { @@ -69,14 +69,14 @@ class FIRToStdTypeConverter : public M::TypeConverter { return fir::RealType::get(ctx, kind); } - /// Convert FIR types to LLVM IR dialect types + /// Convert FIR types to MLIR standard dialect types M::Type convertType(M::Type t) override { if (auto cplx = t.dyn_cast()) { return M::ComplexType::get( kindToRealType(cplx.getContext(), cplx.getFKind())); } if (auto integer = t.dyn_cast()) { - return M::IntegerType::get(integer.getSizeInBits(), integer.getContext()); + return M::IntegerType::get(integer.getFKind() * 8, integer.getContext()); } if (auto real = t.dyn_cast()) { return kindToRealType(real.getContext(), real.getFKind()); diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index 0ff89647a55b..254392e1129e 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -15,6 +15,7 @@ #include "fir/Tilikum/Tilikum.h" #include "fir/Dialect.h" #include "fir/FIROps.h" +#include "fir/KindMapping.h" #include "fir/Type.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h" @@ -37,7 +38,9 @@ /// operations to the LLVM-IR dialect. #undef TODO -#define TODO(X) (void)X; assert(false && "not yet implemented") +#define TODO(X) \ + (void)X; \ + assert(false && "not yet implemented") namespace L = llvm; namespace M = mlir; @@ -53,35 +56,58 @@ using AttributeTy = L::ArrayRef; /// FIR type converter /// This converts FIR types to LLVM types (for now) class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { + KindMapping kindMapping; + static L::StringMap identStructCache; + public: - using LLVMTypeConverter::LLVMTypeConverter; + FIRToLLVMTypeConverter(M::MLIRContext *context) + : LLVMTypeConverter(context), kindMapping(context) {} + // fir.dims --> llvm<"[r x [3 x i64]]"> + // FIXME M::LLVM::LLVMType dimsType() { - auto i64Ty = M::LLVM::LLVMType::getInt64Ty(llvmDialect); - return M::LLVM::LLVMType::getVectorTy(i64Ty, 3); + auto i64Ty{M::LLVM::LLVMType::getInt64Ty(llvmDialect)}; + return M::LLVM::LLVMType::getArrayTy(i64Ty, 3); + } + + // i32 is used here because LLVM wants i32 constants when indexing into struct + // types. Indexing into other aggregate types is more flexible. TODO: See if + // we can't use i64 anyway; the restiction may not longer hold. + M::LLVM::LLVMType indexType() { + return M::LLVM::LLVMType::getInt32Ty(llvmDialect); } - // FIXME: Currently this is a stub. This should correspond to the descriptor - // as defined ISO_Fortran_binding.h and the addendum defined in descriptor.h + // This corresponds to the descriptor as defined ISO_Fortran_binding.h and the + // addendum defined in descriptor.h. + // FIXME: This code should be generated and follow SPOT M::LLVM::LLVMType convertBoxType(BoxType box) { // (buffer*, ele-size, rank, type-descriptor, attribute, [dims]) L::SmallVector parts; - // buffer* M::Type ele = box.getEleTy(); - auto *ctx = box.getContext(); + // auto *ctx = box.getContext(); auto eleTy = unwrap(convertType(ele)); + // buffer* parts.push_back(eleTy.getPointerTo()); // ele-size parts.push_back(M::LLVM::LLVMType::getInt64Ty(llvmDialect)); + // version + parts.push_back(M::LLVM::LLVMType::getInt32Ty(llvmDialect)); // rank - parts.push_back(convertTypeDescType(ctx)); + parts.push_back(M::LLVM::LLVMType::getInt8Ty(llvmDialect)); + // type (code) + parts.push_back(M::LLVM::LLVMType::getInt8Ty(llvmDialect)); // attribute - parts.push_back(M::LLVM::LLVMType::getInt64Ty(llvmDialect)); - // [(int,int,int)] - parts.push_back(dimsType().getPointerTo()); - return M::LLVM::LLVMType::getStructTy(llvmDialect, parts); + parts.push_back(M::LLVM::LLVMType::getInt8Ty(llvmDialect)); + // addendum + parts.push_back(M::LLVM::LLVMType::getInt8Ty(llvmDialect)); + // opt-dims: [0..15 x [int,int,int]] (see fir.dims) + // opt-type-ptr: i8* (see fir.tdesc) + // opt-flags: i64 + // opt-len-params: [? x i64] + return M::LLVM::LLVMType::getStructTy(llvmDialect, parts).getPointerTo(); } + // fir.boxchar --> llvm<"{ ix*, i64 }"> where ix is kind mapping M::LLVM::LLVMType convertBoxCharType(BoxCharType boxchar) { auto ptrTy = convertCharType(boxchar.getEleTy()).getPointerTo(); auto i64Ty = M::LLVM::LLVMType::getInt64Ty(llvmDialect); @@ -89,93 +115,91 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { return M::LLVM::LLVMType::getStructTy(llvmDialect, tuple); } - // fir.boxproc --> llvm<"{ FT*, i8* }"> + // fir.boxproc --> llvm<"{ any*, i8* }"> M::LLVM::LLVMType convertBoxProcType(BoxProcType boxproc) { auto funcTy = convertType(boxproc.getEleTy()); auto ptrTy = unwrap(funcTy).getPointerTo(); auto i8Ty = M::LLVM::LLVMType::getInt8Ty(llvmDialect); - L::SmallVector tuple = {ptrTy, i8Ty}; + L::SmallVector tuple{ptrTy, i8Ty}; return M::LLVM::LLVMType::getStructTy(llvmDialect, tuple); } + // fir.char --> llvm<"ix*"> where ix is scaled by kind mapping M::LLVM::LLVMType convertCharType(CharacterType charTy) { - return convertIntLike(charTy); + return M::LLVM::LLVMType::getIntNTy( + llvmDialect, kindMapping.getCharacterBitsize(charTy.getFKind())); } + // fir.complex --> llvm<"{ anyfloat, anyfloat }"> M::LLVM::LLVMType convertComplexType(KindTy kind) { - auto realTy = convertRealType(kind); - L::SmallVector tuple = {realTy, realTy}; + auto realID = kindMapping.getComplexTypeID(kind); + auto realTy = fromRealTypeID(realID, kind); + L::SmallVector tuple{realTy, realTy}; return M::LLVM::LLVMType::getStructTy(llvmDialect, tuple); } - template - M::LLVM::LLVMType convertIntLike(A intLike) { - return M::LLVM::LLVMType::getIntNTy(llvmDialect, intLike.getSizeInBits()); - } - LLVM::LLVMType getDefaultInt() { // FIXME: this should be tied to the front-end default return M::LLVM::LLVMType::getInt64Ty(llvmDialect); } + // fir.int --> llvm.ix where ix is a kind mapping + M::LLVM::LLVMType convertIntegerType(IntType intTy) { + return M::LLVM::LLVMType::getIntNTy( + llvmDialect, kindMapping.getIntegerBitsize(intTy.getFKind())); + } + + // fir.logical --> llvm.ix where ix is a kind mapping + M::LLVM::LLVMType convertLogicalType(LogicalType boolTy) { + return M::LLVM::LLVMType::getIntNTy( + llvmDialect, kindMapping.getLogicalBitsize(boolTy.getFKind())); + } + template M::LLVM::LLVMType convertPointerLike(A &ty) { - auto eleTy = unwrap(convertType(ty.getEleTy())); - return eleTy.getPointerTo(); + return unwrap(convertType(ty.getEleTy())).getPointerTo(); } // convert a front-end kind value to either a std or LLVM IR dialect type + // fir.real --> llvm.anyfloat where anyfloat is a kind mapping M::LLVM::LLVMType convertRealType(KindTy kind) { - auto *mlirContext = llvmDialect->getContext(); - switch (kind) { - case 2: - return M::LLVM::LLVMType::getHalfTy(llvmDialect); - case 3: - emitError(UnknownLoc::get(mlirContext), - "unsupported type: !fir.real<3>, BF16"); - return {}; - case 4: - return M::LLVM::LLVMType::getFloatTy(llvmDialect); - case 8: - return M::LLVM::LLVMType::getDoubleTy(llvmDialect); - case 10: - return M::LLVM::LLVMType::getX86_FP80Ty(llvmDialect); - case 16: - return M::LLVM::LLVMType::getFP128Ty(llvmDialect); - } - emitError(UnknownLoc::get(mlirContext)) - << "unsupported type: !fir.real<" << kind << ">"; - return {}; + return fromRealTypeID(kindMapping.getRealTypeID(kind), kind); } + // The cache is needed to keep a unique mapping from name -> StructType M::LLVM::LLVMType convertRecordType(RecordType derived) { - TODO(0); - return {}; - } - + auto name{derived.getName()}; + auto iter{identStructCache.find(name)}; + if (iter != identStructCache.end()) + return iter->second; + auto st{M::LLVM::LLVMType::createStructTy(llvmDialect, name)}; + identStructCache[name] = st; + L::SmallVector members; + for (auto mem : derived.getTypeList()) + members.push_back(convertType(mem.second).cast()); + M::LLVM::LLVMType::setStructTyBody(st, members); + return st; + } + + // fir.array --> llvm<"[...[c x any]]"> M::LLVM::LLVMType convertSequenceType(SequenceType seq) { - // FIXME: we don't want to lower to std.memref type - auto shape = seq.getShape(); - auto eleTy = unwrap(convertType(seq.getEleTy())); - L::SmallVector memshape; - if (shape.hasValue()) { - for (auto bi : *shape) { - if (bi.hasValue()) { - memshape.push_back(*bi); - } else { - memshape.push_back(-1); // unknown shape - } - } + auto baseTy = unwrap(convertType(seq.getEleTy())); + if (auto shape = seq.getShape()) { + for (auto e : shape.getValue()) + if (e.hasValue()) + baseTy = M::LLVM::LLVMType::getArrayTy(baseTy, e.getValue()); + else + return baseTy.getPointerTo(); + return baseTy; } - std::reverse(memshape.begin(), memshape.end()); - return {}; + return baseTy.getPointerTo(); } - // lower the type descriptor + // fir.tdesc --> llvm<"i8*"> + // FIXME: for now use a void*, however pointer identity is not sufficient for + // the f18 object v. class distinction M::LLVM::LLVMType convertTypeDescType(M::MLIRContext *ctx) { - // FIXME: using an i64* for now - auto i64Ty = M::LLVM::LLVMType::getInt64Ty(llvmDialect); - return i64Ty.getPointerTo(); + return M::LLVM::LLVMType::getInt8PtrTy(llvmDialect); } /// Convert FIR types to LLVM IR dialect types @@ -199,9 +223,9 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { if (auto heap = t.dyn_cast()) return convertPointerLike(heap); if (auto integer = t.dyn_cast()) - return convertIntLike(integer); - if (auto log = t.dyn_cast()) - return convertIntLike(log); + return convertIntegerType(integer); + if (auto logical = t.dyn_cast()) + return convertLogicalType(logical); if (auto pointer = t.dyn_cast()) return convertPointerLike(pointer); if (auto real = t.dyn_cast()) @@ -215,7 +239,27 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { return LLVMTypeConverter::convertType(t); } - // cloned from LLVMTypeConverter since this is private there + /// Convert llvm::Type::TypeID to mlir::LLVM::LLVMType + M::LLVM::LLVMType fromRealTypeID(L::Type::TypeID typeID, KindTy kind) { + switch (typeID) { + case L::Type::TypeID::HalfTyID: + return M::LLVM::LLVMType::getHalfTy(llvmDialect); + case L::Type::TypeID::FloatTyID: + return M::LLVM::LLVMType::getFloatTy(llvmDialect); + case L::Type::TypeID::DoubleTyID: + return M::LLVM::LLVMType::getDoubleTy(llvmDialect); + case L::Type::TypeID::X86_FP80TyID: + return M::LLVM::LLVMType::getX86_FP80Ty(llvmDialect); + case L::Type::TypeID::FP128TyID: + return M::LLVM::LLVMType::getFP128Ty(llvmDialect); + default: + emitError(UnknownLoc::get(llvmDialect->getContext())) + << "unsupported type: !fir.real<" << kind << ">"; + return {}; + } + } + + /// HACK: cloned from LLVMTypeConverter since this is private there LLVM::LLVMType unwrap(Type type) { if (!type) return nullptr; @@ -228,6 +272,9 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { } }; +// instantiate static data member +L::StringMap FIRToLLVMTypeConverter::identStructCache; + L::SmallVector pruneNamedAttrDict(L::ArrayRef attrs, L::ArrayRef omitNames) { @@ -284,8 +331,12 @@ struct AllocaOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto alloc = M::cast(op); + auto loc = alloc.getLoc(); auto ty = lowering.convertType(alloc.getType()); - rewriter.replaceOpWithNewOp(alloc, ty, operands, + auto ity = lowering.indexType(); + auto c1attr = rewriter.getI32IntegerAttr(1); + auto c1 = rewriter.create(loc, ity, c1attr); + rewriter.replaceOpWithNewOp(alloc, ty, c1.getResult(), alloc.getAttrs()); return matchSuccess(); } @@ -314,8 +365,21 @@ struct BoxAddrOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxaddr = M::cast(op); - // FIXME: stub. needs a test, etc. - rewriter.replaceOpWithNewOp(boxaddr, M::Type{}, operands); + auto a = operands[0]; + auto loc = boxaddr.getLoc(); + auto ty = lowering.convertType(boxaddr.getType()); + auto c0attr = rewriter.getI32IntegerAttr(0); + if (auto argty = boxaddr.val()->getType().dyn_cast()) { + auto ity = lowering.indexType(); + auto c0 = rewriter.create(loc, ity, c0attr); + L::SmallVector args({a, c0, c0}); + auto pty = lowering.unwrap(ty).getPointerTo(); + auto p = rewriter.create(loc, pty, args); + rewriter.replaceOpWithNewOp(boxaddr, ty, p); + } else { + auto c0 = M::ArrayAttr::get(c0attr, boxaddr.getContext()); + rewriter.replaceOpWithNewOp(boxaddr, ty, a, c0); + } return matchSuccess(); } }; @@ -327,7 +391,11 @@ struct BoxCharLenOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxchar = M::cast(op); - TODO(boxchar); + auto a = operands[0]; + auto ty = lowering.convertType(boxchar.getType()); + auto ctx = boxchar.getContext(); + auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); + rewriter.replaceOpWithNewOp(boxchar, ty, a, c1); return matchSuccess(); } }; @@ -339,7 +407,18 @@ struct BoxDimsOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxdims = M::cast(op); - TODO(boxdims); + auto a = operands[0]; + auto dim = operands[1]; + auto loc = boxdims.getLoc(); + auto ity = lowering.indexType(); + auto c0attr = rewriter.getI32IntegerAttr(0); + auto c0 = rewriter.create(loc, ity, c0attr); + auto c7attr = rewriter.getI32IntegerAttr(7); + auto c7 = rewriter.create(loc, ity, c7attr); + auto ty = lowering.convertType(boxdims.getResult(0)->getType()); + L::SmallVector args({a, c0, c7, dim}); + auto p = rewriter.create(loc, ty, args); + rewriter.replaceOpWithNewOp(boxdims, ty, p); return matchSuccess(); } }; @@ -351,7 +430,17 @@ struct BoxEleSizeOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxelesz = M::cast(op); - TODO(boxelesz); + auto a = operands[0]; + auto loc = boxelesz.getLoc(); + auto ty = lowering.convertType(boxelesz.getType()); + auto ity = lowering.indexType(); + auto c0attr = rewriter.getI32IntegerAttr(0); + auto c0 = rewriter.create(loc, ity, c0attr); + auto c1attr = rewriter.getI32IntegerAttr(1); + auto c1 = rewriter.create(loc, ity, c1attr); + L::SmallVector args({a, c0, c1}); + auto p = rewriter.create(loc, ty, args); + rewriter.replaceOpWithNewOp(boxelesz, ty, p); return matchSuccess(); } }; @@ -363,7 +452,22 @@ struct BoxIsAllocOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxisalloc = M::cast(op); - TODO(boxisalloc); + auto a = operands[0]; + auto loc = boxisalloc.getLoc(); + auto ty = lowering.convertType(boxisalloc.getType()); + auto ity = lowering.indexType(); + auto c0attr = rewriter.getI32IntegerAttr(0); + auto c0 = rewriter.create(loc, ity, c0attr); + auto c5attr = rewriter.getI32IntegerAttr(5); + auto c5 = rewriter.create(loc, ity, c5attr); + L::SmallVector args({a, c0, c5}); + auto p = rewriter.create(loc, ty, args); + auto ld = rewriter.create(loc, ty, p); + auto c2attr = rewriter.getI32IntegerAttr(2); + auto ab = rewriter.create(loc, ity, c2attr); + auto bit = rewriter.create(loc, ity, ld, ab); + rewriter.replaceOpWithNewOp( + boxisalloc, M::LLVM::ICmpPredicate::ne, bit, c0); return matchSuccess(); } }; @@ -375,7 +479,19 @@ struct BoxIsArrayOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxisarray = M::cast(op); - TODO(boxisarray); + auto a = operands[0]; + auto loc = boxisarray.getLoc(); + auto ty = lowering.convertType(boxisarray.getType()); + auto ity = lowering.indexType(); + auto c0attr = rewriter.getI32IntegerAttr(0); + auto c0 = rewriter.create(loc, ity, c0attr); + auto c3attr = rewriter.getI32IntegerAttr(3); + auto c3 = rewriter.create(loc, ity, c3attr); + L::SmallVector args({a, c0, c3}); + auto p = rewriter.create(loc, ty, args); + auto ld = rewriter.create(loc, ty, p); + rewriter.replaceOpWithNewOp( + boxisarray, M::LLVM::ICmpPredicate::ne, ld, c0); return matchSuccess(); } }; @@ -387,7 +503,22 @@ struct BoxIsPtrOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxisptr = M::cast(op); - TODO(boxisptr); + auto a = operands[0]; + auto loc = boxisptr.getLoc(); + auto ty = lowering.convertType(boxisptr.getType()); + auto ity = lowering.indexType(); + auto c0attr = rewriter.getI32IntegerAttr(0); + auto c0 = rewriter.create(loc, ity, c0attr); + auto c5attr = rewriter.getI32IntegerAttr(5); + auto c5 = rewriter.create(loc, ity, c5attr); + L::SmallVector args({a, c0, c5}); + auto p = rewriter.create(loc, ty, args); + auto ld = rewriter.create(loc, ty, p); + auto c1attr = rewriter.getI32IntegerAttr(1); + auto ab = rewriter.create(loc, ity, c1attr); + auto bit = rewriter.create(loc, ity, ld, ab); + rewriter.replaceOpWithNewOp( + boxisptr, M::LLVM::ICmpPredicate::ne, bit, c0); return matchSuccess(); } }; @@ -399,7 +530,12 @@ struct BoxProcHostOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxprochost = M::cast(op); - TODO(boxprochost); + auto a = operands[0]; + auto ty = lowering.convertType(boxprochost.getType()); + auto ctx = boxprochost.getContext(); + auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); + rewriter.replaceOpWithNewOp(boxprochost, ty, a, + c1); return matchSuccess(); } }; @@ -411,7 +547,18 @@ struct BoxRankOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxrank = M::cast(op); - TODO(boxrank); + auto a = operands[0]; + auto loc = boxrank.getLoc(); + auto ty = lowering.convertType(boxrank.getType()); + auto ity = lowering.indexType(); + auto c0attr = rewriter.getI32IntegerAttr(0); + auto c0 = rewriter.create(loc, ity, c0attr); + auto c3attr = rewriter.getI32IntegerAttr(3); + auto c3 = rewriter.create(loc, ity, c3attr); + L::SmallVector args({a, c0, c3}); + auto pty = lowering.unwrap(ty).getPointerTo(); + auto p = rewriter.create(loc, pty, args); + rewriter.replaceOpWithNewOp(boxrank, ty, p); return matchSuccess(); } }; @@ -423,11 +570,25 @@ struct BoxTypeDescOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto boxtypedesc = M::cast(op); - TODO(boxtypedesc); + auto a = operands[0]; + auto loc = boxtypedesc.getLoc(); + auto ty = lowering.convertType(boxtypedesc.getType()); + auto ity = lowering.indexType(); + auto c0attr = rewriter.getI32IntegerAttr(0); + auto c0 = rewriter.create(loc, ity, c0attr); + auto c4attr = rewriter.getI32IntegerAttr(4); + auto c4 = rewriter.create(loc, ity, c4attr); + L::SmallVector args({a, c0, c4}); + auto pty = lowering.unwrap(ty).getPointerTo(); + auto p = rewriter.create(loc, pty, args); + auto ld = rewriter.create(loc, ty, p); + auto i8ptr = M::LLVM::LLVMType::getInt8PtrTy(getDialect()); + rewriter.replaceOpWithNewOp(boxtypedesc, i8ptr, ld); return matchSuccess(); } }; +// direct call LLVM function struct CallOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -435,7 +596,11 @@ struct CallOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto call = M::cast(op); - TODO(call); + L::SmallVector resultTys; + for (auto r : call.getResults()) + resultTys.push_back(lowering.convertType(r->getType())); + rewriter.replaceOpWithNewOp(call, resultTys, operands, + call.getAttrs()); return matchSuccess(); } }; @@ -533,6 +698,9 @@ struct DispatchOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto dispatch = M::cast(op); + auto ty = lowering.convertType(dispatch.getFunctionType()); + // get the table, lookup the method, fetch the func-ptr + rewriter.replaceOpWithNewOp(dispatch, ty, operands); TODO(dispatch); return matchSuccess(); } @@ -572,7 +740,17 @@ struct EmboxCharOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto emboxchar = M::cast(op); - TODO(emboxchar); + auto a = operands[0]; + auto b = operands[1]; + auto loc = emboxchar.getLoc(); + auto ctx = emboxchar.getContext(); + auto ty = lowering.convertType(emboxchar.getType()); + auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); + auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); + auto un = rewriter.create(loc, ty); + auto r = rewriter.create(loc, ty, un, a, c0); + rewriter.replaceOpWithNewOp(emboxchar, ty, r, b, + c1); return matchSuccess(); } }; @@ -598,7 +776,17 @@ struct EmboxProcOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto emboxproc = M::cast(op); - TODO(emboxproc); + auto a = operands[0]; + auto b = operands[1]; + auto loc = emboxproc.getLoc(); + auto ctx = emboxproc.getContext(); + auto ty = lowering.convertType(emboxproc.getType()); + auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); + auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); + auto un = rewriter.create(loc, ty); + auto r = rewriter.create(loc, ty, un, a, c0); + rewriter.replaceOpWithNewOp(emboxproc, ty, r, b, + c1); return matchSuccess(); } }; @@ -616,6 +804,10 @@ struct ExtractValueOpConversion : public FIROpConversion { } }; +// Compute the offset of a field in a variable of derived type. A value of type +// field can only be used as an argument to a coordinate_of, extract_value, or +// insert_value operation. It derives it's meaning from the context of where it +// is used. struct FieldIndexOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -623,7 +815,7 @@ struct FieldIndexOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto fieldindex = M::cast(op); - TODO(fieldindex); + rewriter.replaceOp(fieldindex, {}); return matchSuccess(); } }; @@ -664,12 +856,12 @@ struct FreeMemOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto freemem = M::cast(op); - M::FuncOp freeFunc = genFreeFunc(freemem, rewriter); + auto freeFunc = genFreeFunc(freemem, rewriter); M::Value *casted = rewriter.create( freemem.getLoc(), getVoidPtrType(), operands[0]); + auto sym = rewriter.getSymbolRefAttr(freeFunc); rewriter.replaceOpWithNewOp( - freemem, llvm::ArrayRef(), rewriter.getSymbolRefAttr(freeFunc), - casted); + freemem, llvm::ArrayRef(), sym, casted); return matchSuccess(); } }; @@ -711,8 +903,7 @@ struct GlobalEntryOpConversion : public FIROpConversion { } }; -class GlobalOpConversion : public FIROpConversion { -public: +struct GlobalOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; M::PatternMatchResult @@ -744,11 +935,13 @@ struct ICallOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto icall = M::cast(op); - TODO(icall); + rewriter.replaceOpWithNewOp(icall, operands); return matchSuccess(); } }; +// InsertValue is the generalized instruction for the composition of new +// aggregate type values. struct InsertValueOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -762,7 +955,11 @@ struct InsertValueOpConversion : public FIROpConversion { } }; -// compute the index of the LEN param in the descriptor addendum +// Compute the index of the LEN param in the descriptor addendum. A value of +// type field can only be used as an argument to a coordinate_of, extract_value, +// or insert_value operation. It derives it's meaning from the context of where +// it is used. A LEN parameter cannot be an aggregate itself and thus a +// LenParamIndexOp can appear only once and must be last in the argument list. struct LenParamIndexOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -771,7 +968,7 @@ struct LenParamIndexOpConversion matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto lenparam = M::cast(op); - TODO(lenparam); + rewriter.replaceOp(lenparam, {}); return matchSuccess(); } }; @@ -784,12 +981,9 @@ struct LoadOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto load = M::cast(op); - auto newLoad = rewriter.create( - load.getLoc(), lowering.convertType(load.getType()), operands, - load.getAttrs()); - // ???: the next line works around a bug [do we still need this?] - load.replaceAllUsesWith(newLoad.getResult()); - rewriter.replaceOp(op, newLoad.getResult()); + auto ty = lowering.convertType(load.getType()); + auto at = load.getAttrs(); + rewriter.replaceOpWithNewOp(op, ty, operands, at); return matchSuccess(); } }; @@ -807,6 +1001,7 @@ struct LoopOpConversion : public FIROpConversion { } }; +// TODO: how do we want to enforce this in LLVM-IR? struct NoReassocOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -814,7 +1009,8 @@ struct NoReassocOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto noreassoc = M::cast(op); - TODO(noreassoc); + noreassoc.replaceAllUsesWith(operands[0]); + rewriter.replaceOp(noreassoc, {}); return matchSuccess(); } }; @@ -941,9 +1137,8 @@ struct UndefOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto undef = M::cast(op); - M::Value *v{rewriter.create( - undef.getLoc(), lowering.convertType(undef.getType()))}; - rewriter.replaceOp(op, v); + rewriter.replaceOpWithNewOp( + undef, lowering.convertType(undef.getType())); return matchSuccess(); } }; @@ -955,10 +1150,11 @@ struct UnreachableOpConversion : public FIROpConversion { M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { + auto unreach = M::cast(op); L::SmallVector destinations; // none L::SmallVector destOperands; // none - rewriter.create( - op->getLoc(), operands, destinations, destOperands, op->getAttrs()); + rewriter.replaceOpWithNewOp( + unreach, operands, destinations, destOperands, unreach.getAttrs()); return matchSuccess(); } }; @@ -978,11 +1174,11 @@ struct WhereOpConversion : public FIROpConversion { // Generate code for complex addition/subtraction template -M::LLVM::InsertValueOp complexSum(OPTY sumop, +M::LLVM::InsertValueOp complexSum(OPTY sumop, OperandTy opnds, M::ConversionPatternRewriter &rewriter, FIRToLLVMTypeConverter &lowering) { - auto a = sumop.lhs(); - auto b = sumop.rhs(); + auto a = opnds[0]; + auto b = opnds[1]; auto loc = sumop.getLoc(); auto ctx = sumop.getContext(); auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); @@ -1072,7 +1268,7 @@ struct AddcOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { // result: (x + x') + i(y + y') auto addc = cast(op); - auto r = complexSum(addc, rewriter, lowering); + auto r = complexSum(addc, operands, rewriter, lowering); addc.replaceAllUsesWith(r.getResult()); rewriter.replaceOp(addc, r.getResult()); return matchSuccess(); @@ -1087,7 +1283,7 @@ struct SubcOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { // result: (x - x') + i(y - y') auto subc = M::cast(op); - auto r = complexSum(subc, rewriter, lowering); + auto r = complexSum(subc, operands, rewriter, lowering); subc.replaceAllUsesWith(r.getResult()); rewriter.replaceOp(subc, r.getResult()); return matchSuccess(); @@ -1103,8 +1299,8 @@ struct MulcOpConversion : public FIROpConversion { auto mulc = M::cast(op); // TODO: should this just call __muldc3 ? // result: (xx'-yy')+i(xy'+yx') - auto a = mulc.lhs(); - auto b = mulc.rhs(); + auto a = operands[0]; + auto b = operands[1]; auto loc = mulc.getLoc(); auto ctx = mulc.getContext(); auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); @@ -1138,8 +1334,8 @@ struct DivcOpConversion : public FIROpConversion { auto divc = M::cast(op); // TODO: should this just call __divdc3 ? // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' - auto a = divc.lhs(); - auto b = divc.rhs(); + auto a = operands[0]; + auto b = operands[1]; auto loc = divc.getLoc(); auto ctx = divc.getContext(); auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); @@ -1202,8 +1398,7 @@ inline void rewriteSelectConstruct(M::Operation *op, OperandTy operands, /// /// This pass lowers all FIR dialect operations to LLVM IR dialect. An /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. -class FIRToLLVMLoweringPass : public M::ModulePass { -public: +struct FIRToLLVMLoweringPass : public M::ModulePass { void runOnModule() override { auto &context{getContext()}; FIRToLLVMTypeConverter typeConverter{&context}; diff --git a/lib/fir/Type.cpp b/lib/fir/Type.cpp index 834b7408d610..57b36a1620d4 100644 --- a/lib/fir/Type.cpp +++ b/lib/fir/Type.cpp @@ -1231,10 +1231,10 @@ bool fir::isa_fir_or_std_type(mlir::Type t) { // CHARACTER CharacterType fir::CharacterType::get(M::MLIRContext *ctxt, KindTy kind) { - return Base::get(ctxt, FIR_CHARACTER, kind * 8); + return Base::get(ctxt, FIR_CHARACTER, kind); } -int fir::CharacterType::getSizeInBits() const { return getImpl()->getFKind(); } +int fir::CharacterType::getFKind() const { return getImpl()->getFKind(); } // Dims @@ -1253,35 +1253,34 @@ FieldType fir::FieldType::get(M::MLIRContext *ctxt, KindTy) { // LOGICAL LogicalType fir::LogicalType::get(M::MLIRContext *ctxt, KindTy kind) { - return Base::get(ctxt, FIR_LOGICAL, kind * 8); + return Base::get(ctxt, FIR_LOGICAL, kind); } -int fir::LogicalType::getSizeInBits() const { return getImpl()->getFKind(); } +int fir::LogicalType::getFKind() const { return getImpl()->getFKind(); } // INTEGER IntType fir::IntType::get(M::MLIRContext *ctxt, KindTy kind) { - return Base::get(ctxt, FIR_INT, kind * 8); + return Base::get(ctxt, FIR_INT, kind); } -int fir::IntType::getSizeInBits() const { return getImpl()->getFKind(); } +int fir::IntType::getFKind() const { return getImpl()->getFKind(); } // COMPLEX CplxType fir::CplxType::get(M::MLIRContext *ctxt, KindTy kind) { - return Base::get(ctxt, FIR_COMPLEX, kind * 8); + return Base::get(ctxt, FIR_COMPLEX, kind); } -int fir::CplxType::getSizeInBits() const { return getImpl()->getFKind() * 2; } -KindTy fir::CplxType::getFKind() const { return getImpl()->getFKind() / 8; } +KindTy fir::CplxType::getFKind() const { return getImpl()->getFKind(); } // REAL RealType fir::RealType::get(M::MLIRContext *ctxt, KindTy kind) { - return Base::get(ctxt, FIR_REAL, kind * 8); + return Base::get(ctxt, FIR_REAL, kind); } -int fir::RealType::getSizeInBits() const { return getImpl()->getFKind(); } +int fir::RealType::getFKind() const { return getImpl()->getFKind(); } // Box @@ -1306,7 +1305,7 @@ fir::BoxType::verifyConstructionInvariants(L::Optional, // BoxChar BoxCharType fir::BoxCharType::get(M::MLIRContext *ctxt, KindTy kind) { - return Base::get(ctxt, FIR_BOXCHAR, kind * 8); + return Base::get(ctxt, FIR_BOXCHAR, kind); } CharacterType fir::BoxCharType::getEleTy() const { From 2df2f892da6d3b44528c1f8fca82410a74099c06 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Mon, 21 Oct 2019 10:32:41 -0700 Subject: [PATCH 016/123] fix type passed to InsertValueOp and ExtractValueOp builder --- lib/burnside/convert-expr.cc | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 9b7e8d7161b4..afb398c1abb6 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -243,11 +243,8 @@ class ExprLowering { auto *ctxt = builder.getContext(); auto realTy{getFIRType(ctxt, defaults, RealCat, KIND)}; auto index = genIntegerConstant<4>(ctxt, part.isImaginaryPart ? 1 : 0); - L::SmallVector input({fir::CplxType::get(ctxt, KIND)}); - L::SmallVector output({realTy}); - auto funcTy = M::FunctionType::get(input, output, ctxt); return builder.create( - getLoc(), funcTy, genval(part.left()), index); + getLoc(), realTy, genval(part.left()), index); } template M::Value *genval(Ev::Negate> const &) { @@ -317,17 +314,14 @@ class ExprLowering { auto *ctxt = builder.getContext(); auto complexTy{fir::CplxType::get(ctxt, KIND)}; auto loc{getLoc()}; - L::SmallVector input( - {getFIRType(ctxt, defaults, RealCat, KIND)}); - L::SmallVector output({complexTy}); - auto fTy = M::FunctionType::get(input, output, builder.getContext()); auto und = builder.create(loc, complexTy); auto *lf = genval(op.left()); auto *realIdx = genIntegerConstant<4>(ctxt, 0); - auto rp = builder.create(loc, fTy, und, lf, realIdx); + auto rp = + builder.create(loc, complexTy, und, lf, realIdx); auto *rt = genval(op.right()); auto *imagIdx = genIntegerConstant<4>(ctxt, 1); - return builder.create(loc, fTy, rp, rt, imagIdx); + return builder.create(loc, complexTy, rp, rt, imagIdx); } template M::Value *genval(Ev::Concat const &op) { // TODO this is a bogus call From 3e54c8dad228f36baf385adc85e514b3b6c50082 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 24 Oct 2019 13:42:27 -0700 Subject: [PATCH 017/123] sync to latest MLIR and get rid of the `fir.icall` operation. All calls can now use the `fir.call` operation. --- include/fir/FIROps.td | 102 ++++++------------------------------------ lib/fir/FIROps.cpp | 70 +++++++++++++++++++++++++---- lib/fir/Tilikum.cpp | 53 +++++++++++++--------- test/fir/fir-ops.fir | 24 +++++----- 4 files changed, 119 insertions(+), 130 deletions(-) diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index b84a8a572d51..641beea160ba 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -125,7 +125,7 @@ def fir_AllocateOpBuilder : OpBuilder< "ArrayRef sizes = {}, ArrayRef attributes = {}", [{ result.addTypes(getRefTy(inType)); - result.addAttribute("in_type", builder->getTypeAttr(inType)); + result.addAttribute("in_type", M::TypeAttr::get(inType)); result.addOperands(sizes); for (auto namedAttr : attributes) result.addAttribute(namedAttr.first, namedAttr.second); @@ -136,7 +136,7 @@ def fir_NamedAllocateOpBuilder : OpBuilder< "ArrayRef sizes = {}, ArrayRef attributes = {}", [{ result.addTypes(getRefTy(inType)); - result.addAttribute("in_type", builder->getTypeAttr(inType)); + result.addAttribute("in_type", M::TypeAttr::get(inType)); result.addAttribute("name", builder->getStringAttr(name)); result.addOperands(sizes); for (auto namedAttr : attributes) @@ -185,7 +185,7 @@ class fir_AllocatableOp traits =[]> : if (parser.parseType(intype)) return M::failure(); auto &builder = parser.getBuilder(); - result.addAttribute("in_type", builder.getTypeAttr(intype)); + result.addAttribute("in_type", M::TypeAttr::get(intype)); if (!parser.parseOptionalComma()) { llvm::SmallVector typeVec; llvm::SmallVector operands; @@ -1473,91 +1473,18 @@ def fir_WhereOp : fir_Op<"where", [ImplicitFirTerminator]> { // Procedure call operations -def fir_CallOp : fir_Op<"call", []>, - Results<(outs Variadic)>, - Arguments<(ins SymbolRefAttr:$callee, Variadic:$args)> { - let summary = "call a procedure directly"; +def fir_CallOp : fir_Op<"call", []>, Results<(outs Variadic)>, + Arguments<(ins OptionalAttr:$callee, + Variadic:$args)> { + let summary = "call a procedure"; let description = [{ - Provides a custom parser and pretty printer to allow for a slightly more - readable syntax in the FIR dialect, e.g. `fir.call @sub(%12)`. + 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)`. }]; - let parser = [{ - M::FunctionType calleeType; - M::SymbolRefAttr proc; - L::SmallVector operands; - if (parser.parseAttribute(proc, "callee", result.attributes) || - parser.parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || - parser.parseOptionalAttributeDict(result.attributes) || - parser.parseColonType(calleeType) || - parser.resolveOperands(operands, calleeType.getInputs(), - parser.getNameLoc(), result.operands) || - parser.addTypesToList(calleeType.getResults(), result.types)) - return M::failure(); - return M::success(); - }]; - let printer = [{ - p << getOperationName() << ' ' << getAttr("callee") << '('; - p.printOperands(args()); - p << ')'; - p.printOptionalAttrDict(getAttrs(), {"callee"}); - p << " : "; - p.printFunctionalType(getOperation()); - }]; -} - -def fir_ICallOp : fir_Op<"icall", []>, - Results<(outs Variadic)>, - Arguments<(ins FunctionType:$proc, Variadic:$args)> { - let summary = "call a procedure indirectly"; - - let description = [{ - Provides a custom parser and pretty printer to allow for a slightly more - readable syntax in the FIR dialect, e.g. `fir.icall %fun(%12)`. - }]; - - let parser = [{ - M::FunctionType calleeType; - L::SmallVector operands; - M::OpAsmParser::OperandType callee; - if (parser.parseOperand(callee)) - return M::failure(); - operands.push_back(callee); - auto calleeLoc = parser.getNameLoc(); - if (parser.parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || - parser.parseOptionalAttributeDict(result.attributes) || - parser.parseColonType(calleeType) || - parser.addTypesToList(calleeType.getResults(), result.types)) - return M::failure(); - L::SmallVector types; - types.push_back(calleeType); - auto input = calleeType.getInputs(); - types.insert(types.end(), input.begin(), input.end()); - if (parser.resolveOperands(operands, types, calleeLoc, result.operands)) - return M::failure(); - return M::success(); - }]; - let printer = [{ - p << getOperationName() << ' '; - p.printOperand(proc()); - p << '('; - p.printOperands(args()); - p << ')'; - p.printOptionalAttrDict(getAttrs(), {}); - p << " : " << getCallee()->getType(); - }]; - let extraClassDeclaration = [{ - mlir::FunctionType getFunctionType(); - Value *getCallee() { return getOperand(0); } - - operand_range getArgOperands() { - return {arg_operand_begin(), arg_operand_end()}; - } - - operand_iterator arg_operand_begin() { return ++operand_begin(); } - operand_iterator arg_operand_end() { return operand_end(); } - }]; + let parser = [{ return parseCallOp(parser, result); }]; + let printer = [{ printCallOp(p, *this); }]; } def fir_DispatchOp : fir_Op<"dispatch", []>, @@ -1590,7 +1517,7 @@ def fir_DispatchOp : fir_Op<"dispatch", []>, parser.resolveOperands( operands, calleeType.getInputs(), calleeLoc, result.operands)) return M::failure(); - result.addAttribute("fn_type", parser.getBuilder().getTypeAttr(calleeType)); + result.addAttribute("fn_type", M::TypeAttr::get(calleeType)); return M::success(); }]; @@ -1731,8 +1658,7 @@ def fir_GenTypeDescOp : fir_OneResultOp<"gentypedesc", [NoSideEffect]> { M::Type intype; if (parser.parseType(intype)) return M::failure(); - auto &builder = parser.getBuilder(); - result.addAttribute("in_type", builder.getTypeAttr(intype)); + result.addAttribute("in_type", M::TypeAttr::get(intype)); M::Type restype; if (parser.parseColonType(restype) || parser.addTypeToList(restype, result.types)) @@ -1801,7 +1727,7 @@ def fir_GlobalEntryOp : fir_Op<"global_entry", []>, parser.parseColonType(type) || parser.resolveOperand(constant, type, result.operands)) return M::failure(); - result.addAttribute("in_type", parser.getBuilder().getTypeAttr(type)); + result.addAttribute("in_type", M::TypeAttr::get(type)); return M::success(); }]; let printer = [{ diff --git a/lib/fir/FIROps.cpp b/lib/fir/FIROps.cpp index cded90c33290..25a99b0656c0 100644 --- a/lib/fir/FIROps.cpp +++ b/lib/fir/FIROps.cpp @@ -44,6 +44,66 @@ M::Type AllocMemOp::getRefTy(M::Type ty) { return HeapType::get(ty); } // CallOp +void printCallOp(M::OpAsmPrinter &p, fir::CallOp &op) { + auto callee = op.callee(); + bool isDirect = callee.hasValue(); + p << op.getOperationName() << ' '; + if (isDirect) + p.printSymbolName(callee.getValue()); + else + p << *op.getOperand(0); + p << '('; + p.printOperands(L::drop_begin(op.getOperands(), isDirect ? 0 : 1)); + p << ')'; + p.printOptionalAttrDict(op.getAttrs(), {"callee"}); + L::SmallVector resultTypes(op.getResultTypes()); + L::SmallVector argTypes( + L::drop_begin(op.getOperandTypes(), isDirect ? 0 : 1)); + p << " : " << FunctionType::get(argTypes, resultTypes, op.getContext()); +} + +M::ParseResult parseCallOp(M::OpAsmParser &parser, M::OperationState &result) { + L::SmallVector operands; + + if (parser.parseOperandList(operands)) + return M::failure(); + bool isDirect = operands.empty(); + SmallVector attrs; + SymbolRefAttr funcAttr; + + if (isDirect) + if (parser.parseAttribute(funcAttr, "callee", attrs)) + return M::failure(); + Type type; + + if (parser.parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || + parser.parseOptionalAttributeDict(attrs) || parser.parseColon() || + parser.parseType(type)) + return M::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 M::failure(); + } else { + auto funcArgs = + L::ArrayRef(operands).drop_front(); + L::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 M::failure(); + } + result.addTypes(funcType.getResults()); + result.attributes = attrs; + return M::success(); +} + // DispatchOp M::FunctionType DispatchOp::getFunctionType() { @@ -110,7 +170,7 @@ void DispatchTableOp::appendTableEntry(M::Operation *op) { void GlobalOp::build(M::Builder *builder, M::OperationState *result, L::StringRef name, M::Type type, L::ArrayRef attrs) { - result->addAttribute(getTypeAttrName(), builder->getTypeAttr(type)); + result->addAttribute(getTypeAttrName(), M::TypeAttr::get(type)); result->addAttribute(M::SymbolTable::getSymbolAttrName(), builder->getStringAttr(name)); for (const auto &pair : attrs) { @@ -140,7 +200,7 @@ M::ParseResult GlobalOp::parse(M::OpAsmParser &parser, if (parser.parseColonType(globalType)) { return M::failure(); } - result.addAttribute(getTypeAttrName(), builder.getTypeAttr(globalType)); + result.addAttribute(getTypeAttrName(), M::TypeAttr::get(globalType)); // Parse the optional initializer body. M::Region *body = result.addRegion(); @@ -175,12 +235,6 @@ void GlobalOp::appendInitialValue(M::Operation *op) { M::Region &GlobalOp::front() { return this->getOperation()->getRegion(0); } -// ICallOp - -M::FunctionType ICallOp::getFunctionType() { - return getCallee()->getType().cast(); -} - // LoadOp /// Get the element type of a reference like type; otherwise null diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index 254392e1129e..1ba031a454a4 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -342,6 +342,24 @@ struct AllocaOpConversion : public FIROpConversion { } }; +M::LLVM::LLVMFuncOp getMalloc(AllocMemOp op, + M::ConversionPatternRewriter &rewriter, + M::LLVM::LLVMDialect *dialect) { + auto module = op.getParentOfType(); + auto mallocFunc = module.lookupSymbol("malloc"); + if (!mallocFunc) { + M::OpBuilder moduleBuilder( + op.getParentOfType().getBodyRegion()); + auto voidPtrType = M::LLVM::LLVMType::getInt8PtrTy(dialect); + auto indexType = M::LLVM::LLVMType::getInt64Ty(dialect); + mallocFunc = moduleBuilder.create( + rewriter.getUnknownLoc(), "malloc", + M::LLVM::LLVMType::getFunctionTy(voidPtrType, indexType, + /*isVarArg=*/false)); + } + return mallocFunc; +} + // convert to `call` to the runtime to `malloc` memory struct AllocMemOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -352,7 +370,11 @@ struct AllocMemOpConversion : public FIROpConversion { auto heap = M::cast(op); auto ty = lowering.convertType(heap.getType()); // FIXME: should be a call to malloc - rewriter.replaceOpWithNewOp(heap, ty, operands, + auto loc = heap.getLoc(); + auto ity = lowering.indexType(); + auto c1attr = rewriter.getI32IntegerAttr(1); + auto c1 = rewriter.create(loc, ity, c1attr); + rewriter.replaceOpWithNewOp(heap, ty, c1.getResult(), heap.getAttrs()); return matchSuccess(); } @@ -927,19 +949,6 @@ struct GlobalOpConversion : public FIROpConversion { } }; -// indirect call (via a pointer); see dispatch as well -struct ICallOpConversion : public FIROpConversion { - using FIROpConversion::FIROpConversion; - - M::PatternMatchResult - matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { - auto icall = M::cast(op); - rewriter.replaceOpWithNewOp(icall, operands); - return matchSuccess(); - } -}; - // InsertValue is the generalized instruction for the composition of new // aggregate type values. struct InsertValueOpConversion : public FIROpConversion { @@ -1415,14 +1424,14 @@ struct FIRToLLVMLoweringPass : public M::ModulePass { EmboxOpConversion, EmboxProcOpConversion, FirEndOpConversion, ExtractValueOpConversion, FieldIndexOpConversion, FreeMemOpConversion, GenDimsOpConversion, GenTypeDescOpConversion, GlobalEntryOpConversion, - GlobalOpConversion, ICallOpConversion, InsertValueOpConversion, - LenParamIndexOpConversion, LoadOpConversion, LoopOpConversion, - ModfOpConversion, MulcOpConversion, MulfOpConversion, - NoReassocOpConversion, SelectCaseOpConversion, SelectOpConversion, - SelectRankOpConversion, SelectTypeOpConversion, StoreOpConversion, - SubcOpConversion, SubfOpConversion, UnboxCharOpConversion, - UnboxOpConversion, UnboxProcOpConversion, UndefOpConversion, - UnreachableOpConversion, WhereOpConversion>(&context, typeConverter); + GlobalOpConversion, InsertValueOpConversion, LenParamIndexOpConversion, + LoadOpConversion, LoopOpConversion, ModfOpConversion, MulcOpConversion, + MulfOpConversion, NoReassocOpConversion, SelectCaseOpConversion, + SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion, + StoreOpConversion, SubcOpConversion, SubfOpConversion, + UnboxCharOpConversion, UnboxOpConversion, UnboxProcOpConversion, + UndefOpConversion, UnreachableOpConversion, WhereOpConversion>( + &context, typeConverter); M::populateStdToLLVMConversionPatterns(typeConverter, patterns); M::ConversionTarget target{context}; target.addLegalDialect(); diff --git a/test/fir/fir-ops.fir b/test/fir/fir-ops.fir index 9f2b3ece4050..a8c8fed7cca1 100644 --- a/test/fir/fir-ops.fir +++ b/test/fir/fir-ops.fir @@ -35,7 +35,7 @@ func @oth3() -> !fir.tdesc> func @print_index3(index, index, index) func @user_i64(i64) func @user_tdesc(!fir.tdesc>) -func @store_tuple(tuple) -> () +func @store_tuple(!fir.type) -> () func @get_method_box() -> !fir.box> func @method_impl(!fir.box>) @@ -87,7 +87,7 @@ func @instructions() { fir.call @user_i64(%31) : (i64) -> () fir.freemem %5 : !fir.heap> %32 = fir.call @get_func() : () -> (() -> ()) - fir.icall %32() : () -> () + fir.call %32() : () -> () %33 = fir.address_of (@it1) : !fir.ref<() -> !fir.int<4>> return } @@ -101,21 +101,21 @@ func @boxing_match() { %b3 = fir.undefined !fir.char<1> %4 = fir.emboxchar %3, %c8 : (!fir.ref>, i32) -> !fir.boxchar<1> %5:2 = fir.unboxchar %4 : (!fir.boxchar<1>) -> (!fir.ref>, i32) - %6 = fir.alloca tuple : !fir.ref> - %a1 = fir.undefined tuple + %6 = fir.alloca !fir.type : !fir.ref> + %a1 = fir.undefined !fir.type %z = constant 0 : i32 %c12 = constant 12 : i32 - %a2 = fir.insert_value %a1, %c12, %z : (tuple, i32, i32) -> tuple + %a2 = fir.insert_value %a1, %c12, %z : (!fir.type, i32, i32) -> !fir.type %z1 = constant 1 : i32 %c42 = constant 42.13 : f64 - %a3 = fir.insert_value %a1, %c42, %z1 : (tuple, f64, i32) -> tuple - fir.store %a3 to %6 : !fir.ref> - %7 = fir.emboxproc @method_impl, %6 : ((!fir.box>) -> (), !fir.ref>) -> !fir.boxproc<(!fir.box>) -> ()> - %8:2 = fir.unboxproc %7 : (!fir.boxproc<(!fir.box>) -> ()>) -> ((!fir.box>) -> (), !fir.ref>) + %a3 = fir.insert_value %a1, %c42, %z1 : (!fir.type, f64, i32) -> !fir.type + fir.store %a3 to %6 : !fir.ref> + %7 = fir.emboxproc @method_impl, %6 : ((!fir.box>) -> (), !fir.ref>) -> !fir.boxproc<(!fir.box>) -> ()> + %8:2 = fir.unboxproc %7 : (!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) -> () + %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) : (!fir.type) -> () return } From 69819fe5b4ae3dac438706b9557e2b63ed66fa67 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 24 Oct 2019 14:06:22 -0700 Subject: [PATCH 018/123] Change the readme to explain how to build FIR. Update FIR language document. --- README.md | 164 ++++---------------------------- documentation/FIRLangRef.md | 17 +--- lib/burnside/bridge.cc | 175 +++++++++++++++++++++-------------- lib/burnside/convert-expr.cc | 1 + 4 files changed, 126 insertions(+), 231 deletions(-) diff --git a/README.md b/README.md index 7b97fbeed07e..66cf6dc6a834 100644 --- a/README.md +++ b/README.md @@ -6,166 +6,38 @@ --> -# F18 +# FIR -F18 is a ground-up implementation of a Fortran front end written in modern C++. -F18, when combined with LLVM, is intended to replace the Flang compiler. +Working branch for FIR development. -Flang is a Fortran compiler targeting LLVM. -Visit the [Flang wiki](https://github.com/flang-compiler/flang/wiki) -for more information about Flang. -## Getting Started - -Read more about f18 in the [documentation directory](documentation). -Start with the [compiler overview](documentation/Overview.md). - -To better understand Fortran as a language -and the specific grammar accepted by f18, -read [Fortran For C Programmers](documentation/FortranForCProgrammers.md) -and -f18's specifications of the [Fortran grammar](documentation/f2018-grammar.txt) -and -the [OpenMP grammar](documentation/OpenMP-4.5-grammar.txt). - -Treatment of language extensions is covered -in [this document](documentation/Extensions.md). - -To understand the compilers handling of intrinsics, -see the [discussion of intrinsics](documentation/Intrinsics.md). - -To understand how an f18 program communicates with libraries at runtime, -see the discussion of [runtime descriptors](documentation/RuntimeDescriptor.md). - -If you're interested in contributing to the compiler, -read the [style guide](documentation/C++style.md) -and -also review [how f18 uses modern C++ features](documentation/C++17.md). - -## Building F18 - -### Get the Source Code +1. Get the stuff. ``` -cd where/you/want/the/source -git clone https://github.com/flang-compiler/f18.git + git clone http://llvm.org/git/llvm.git + git clone git@github.com:schweitzpgi/mlir.git + git clone git@github.com:schweitzpgi/f18.git ``` -### Supported C++ compilers - -F18 is written in C++17. - -The code has been compiled and tested with -GCC versions 7.2.0, 7.3.0, 8.1.0, and 8.2.0. +2. Get "on" the right branches. -The code has been compiled and tested with -clang version 7.0 and 8.0 -using either GNU's libstdc++ or LLVM's libc++. - -### LLVM dependency - -F18 uses components from LLVM. - -The instructions to build LLVM can be found at -https://llvm.org/docs/GettingStarted.html. - -We highly recommend using the same compiler to compile both llvm and f18. - -The f18 CMakeList.txt file uses -the variable `LLVM_DIR` to find the installed components. - -To get the correct LLVM libraries included in your f18 build, -define LLVM_DIR on the cmake command line. ``` -LLVM=/lib/cmake/llvm cmake -DLLVM_DIR=$LLVM ... + (cd llvm; git checkout master) + (cd mlir; git checkout f18) + (cd f18; git checkout f18) ``` -where `LLVM_INSTALLATION_DIR` is -the top-level directory -where llvm is installed. - -### Building f18 with GCC - -By default, -cmake will search for g++ on your PATH. -The g++ version must be one of the supported versions -in order to build f18. - -Or, -cmake will use the variable CXX to find the C++ compiler. -CXX should include the full path to the compiler -or a name that will be found on your PATH, -e.g. g++-7.2, assuming g++-7.2 is on your PATH. + +3. Setup the LLVM space for in-tree builds. + +``` + cd llvm/projects ; ln -s ../../mlir . + cd llvm/tools ; ln -s ../../f18 flang ``` -export CXX=g++-7.2 -``` -or -``` -CXX=/opt/gcc-7.2/bin/g++-7.2 cmake ... -``` -There's a third option! -The CMakeList.txt file uses the variable GCC -as the path to the bin directory containing the C++ compiler. -GCC can be defined on the cmake command line -where `` is the path to a GCC installation with bin, lib, etc: -``` -cmake -DGCC= ... -``` +4. Create a build space for cmake and make/ninja -### Building f18 with clang - -To build f18 with clang, -cmake needs to know how to find clang++ -and the GCC library and tools that were used to build clang++. - -The CMakeList.txt file expects either CXX or BUILD_WITH_CLANG to be set. - -CXX should include the full path to clang++ -or clang++ should be found on your PATH. -``` -export CXX=clang++ ``` -BUILD_WITH_CLANG can be defined on the cmake command line -where `` -is the path to a clang installation with bin, lib, etc: + mkdir build; cd build; cmake /path/to/llvm -DCMAKE_BUILD_TYPE=Debug -DLLVM_TARGETS_TO_BUILD=X86 ... ``` -cmake -DBUILD_WITH_CLANG= -``` -Or GCC can be defined on the f18 cmake command line -where `` is the path to a GCC installation with bin, lib, etc: -``` -cmake -DGCC= ... -``` -To use f18 after it is built, -the environment variables PATH and LD_LIBRARY_PATH -must be set to use GCC and its associated libraries. - -### Installation Directory - -To specify a custom install location, -add -`-DCMAKE_INSTALL_PREFIX=` -to the cmake command -where `` -is the path where f18 should be installed. -### Build Types -To create a debug build, -add -`-DCMAKE_BUILD_TYPE=Debug` -to the cmake command. -Debug builds execute slowly. - -To create a release build, -add -`-DCMAKE_BUILD_TYPE=Release` -to the cmake command. -Release builds execute quickly. - -### Build F18 -``` -cd ~/f18/build -cmake -DLLVM_DIR=$LLVM ~/f18/src -make -``` diff --git a/documentation/FIRLangRef.md b/documentation/FIRLangRef.md index ebf49f110833..7d8216dd5e7d 100644 --- a/documentation/FIRLangRef.md +++ b/documentation/FIRLangRef.md @@ -811,7 +811,7 @@ generalized and not restricted to affine loop nests. Example: ```mlir - %78 = fir.icall %75(%74) : !fir.ref + %78 = fir.call %75(%74) : !fir.ref fir.where %56 { fir.store %76 to %78 : !fir.ref } otherwise { @@ -823,26 +823,15 @@ Example: Syntax: fir.call callee ( arg-list ) : func-type -Call the specified function. +Call the specified function or function reference. Example: ```mlir + %89 = fir.call %funcref(%arg0) : (!fir.ref) -> f32 %90 = fir.call @function(%arg1, %arg2) : (!fir.ref, !fir.ref) -> f32 ``` -#### `fir.icall` - -Syntax: fir.icall callee ( arg-list ) : func-type - -Call the specified function reference. - -Example: - -```mlir - %89 = fir.icall %funcref(%arg0) : (!fir.ref) -> f32 -``` - #### `fir.dispatch` Syntax: fir.dispatch method-id ( arg-list ) : func-type diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index a0968a605c5d..c68970f49551 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -224,27 +224,32 @@ class FIRConverter { noInsPt = true; } - void genFIR(const Fl::SwitchIOOp &op); + // IO statement with END, ERR, EOR labels + void genFIR(const Fl::SwitchIOOp &op) { + auto loc{toLocation(op.source)}; + (void)loc; + TODO(); + } // CALL with alt-return value returned void genFIR(const Fl::SwitchOp &op, const Pa::CallStmt &stmt) { auto loc{toLocation(op.source)}; - // FIXME (void)loc; + TODO(); } void genFIR(const Fl::SwitchOp &op, const Pa::ComputedGotoStmt &stmt) { auto loc{toLocation(op.source)}; auto *exp{Se::GetExpr(std::get(stmt.t))}; auto *e1{createFIRExpr(loc, exp)}; - // FIXME (void)e1; + TODO(); } void genFIR(const Fl::SwitchOp &op, const Pa::ArithmeticIfStmt &stmt) { auto loc{toLocation(op.source)}; auto *exp{Se::GetExpr(std::get(stmt.t))}; auto *e1{createFIRExpr(loc, exp)}; - // FIXME (void)e1; + TODO(); } M::Value *fromCaseValue(const M::Location &locs, const Pa::CaseValue &val) { return createFIRExpr(locs, Se::GetExpr(val)); @@ -307,14 +312,14 @@ class FIRConverter { void genLoopEnterFIR(const Pa::ScalarLogicalExpr &logicalExpr, const Pa::NonLabelDoStmt *stmt, const Pa::CharBlock &source) { // See 11.1.7.4.1, para. 2 - // See BuildLoopLatchExpression() + // See BuildLoopHeaderExpression() pushDoContext(stmt); } void genLoopEnterFIR(const Pa::LoopControl::Concurrent &concurrent, const Pa::NonLabelDoStmt *stmt, const Pa::CharBlock &source) { // See 11.1.7.4.2 - // FIXME + TODO(); } void genEnterFIR(const Pa::DoConstruct &construct) { @@ -331,7 +336,8 @@ class FIRConverter { } } template void genEnterFIR(const A &construct) { - // FIXME: add other genEnterFIR() members + // FIXME: add other genEnterFIR() members and delete this stub + TODO(); } void genFIR(const Fl::BeginOp &op) { std::visit([&](auto *construct) { genEnterFIR(*construct); }, op.u); @@ -494,7 +500,9 @@ class FIRConverter { } // Action statements - void genFIR(const Pa::AllocateStmt &stmt); + void genFIR(const Pa::AllocateStmt &stmt) { + TODO(); + } void genFIR(const Pa::AssignmentStmt &stmt) { auto *rhs{Se::GetExpr(std::get(stmt.t))}; auto *lhs{Se::GetExpr(std::get(stmt.t))}; @@ -502,34 +510,90 @@ class FIRConverter { build().create( loc, createFIRExpr(loc, rhs), createFIRAddr(loc, lhs)); } - void genFIR(const Pa::BackspaceStmt &stmt); - void genFIR(const Pa::CallStmt &stmt); - void genFIR(const Pa::CloseStmt &stmt); - void genFIR(const Pa::DeallocateStmt &stmt); - void genFIR(const Pa::EndfileStmt &stmt); - void genFIR(const Pa::EventPostStmt &stmt); - void genFIR(const Pa::EventWaitStmt &stmt); - void genFIR(const Pa::FlushStmt &stmt); - void genFIR(const Pa::FormTeamStmt &stmt); - void genFIR(const Pa::InquireStmt &stmt); - void genFIR(const Pa::LockStmt &stmt); - void genFIR(const Pa::NullifyStmt &stmt); - void genFIR(const Pa::OpenStmt &stmt); - void genFIR(const Pa::PointerAssignmentStmt &stmt); - void genFIR(const Pa::PrintStmt &stmt); - void genFIR(const Pa::ReadStmt &stmt); - void genFIR(const Pa::RewindStmt &stmt); - void genFIR(const Pa::SyncAllStmt &stmt); - void genFIR(const Pa::SyncImagesStmt &stmt); - void genFIR(const Pa::SyncMemoryStmt &stmt); - void genFIR(const Pa::SyncTeamStmt &stmt); - void genFIR(const Pa::UnlockStmt &stmt); - void genFIR(const Pa::WaitStmt &stmt); - void genFIR(const Pa::WhereStmt &stmt); - void genFIR(const Pa::WriteStmt &stmt); - void genFIR(const Pa::ForallStmt &stmt); - void genFIR(AnalysisData &ad, const Pa::AssignStmt &stmt); - void genFIR(const Pa::PauseStmt &stmt); + void genFIR(const Pa::BackspaceStmt &stmt) { + TODO(); + } + void genFIR(const Pa::CallStmt &stmt) { + TODO(); + } + void genFIR(const Pa::CloseStmt &stmt) { + TODO(); + } + void genFIR(const Pa::DeallocateStmt &stmt) { + TODO(); + } + void genFIR(const Pa::EndfileStmt &stmt) { + TODO(); + } + void genFIR(const Pa::EventPostStmt &stmt) { + TODO(); + } + void genFIR(const Pa::EventWaitStmt &stmt) { + TODO(); + } + void genFIR(const Pa::FlushStmt &stmt) { + TODO(); + } + void genFIR(const Pa::FormTeamStmt &stmt){ + TODO(); + } + void genFIR(const Pa::InquireStmt &stmt) { + TODO(); + } + void genFIR(const Pa::LockStmt &stmt) { + TODO(); + } + void genFIR(const Pa::NullifyStmt &stmt) { + TODO(); + } + void genFIR(const Pa::OpenStmt &stmt) { + TODO(); + } + void genFIR(const Pa::PointerAssignmentStmt &stmt){ + TODO(); + } + void genFIR(const Pa::PrintStmt &stmt){ + TODO(); + } + void genFIR(const Pa::ReadStmt &stmt){ + TODO(); + } + void genFIR(const Pa::RewindStmt &stmt) { + TODO(); + } + void genFIR(const Pa::SyncAllStmt &stmt){ + TODO(); + } + void genFIR(const Pa::SyncImagesStmt &stmt){ + TODO(); + } + void genFIR(const Pa::SyncMemoryStmt &stmt){ + TODO(); + } + void genFIR(const Pa::SyncTeamStmt &stmt) { + TODO(); + } + void genFIR(const Pa::UnlockStmt &stmt) { + TODO(); + } + void genFIR(const Pa::WaitStmt &stmt){ + TODO(); + } + void genFIR(const Pa::WhereStmt &stmt){ + TODO(); + } + void genFIR(const Pa::WriteStmt &stmt) { + TODO(); + } + void genFIR(const Pa::ForallStmt &stmt) { + TODO(); + } + void genFIR(AnalysisData &ad, const Pa::AssignStmt &stmt) { + TODO(); + } + void genFIR(const Pa::PauseStmt &stmt) { + TODO(); + } template void translateRoutine( @@ -630,7 +694,9 @@ class FIRConverter { FIRConverter() = delete; template constexpr bool Pre(const A &) { return true; } - template constexpr void Post(const A &) {} + template constexpr void Post(const A &) { + // FIXME: make sure we lower all of the parse tree + } /// Translate the various routines from the parse tree void Post(const Pa::MainProgram &mainp) { @@ -793,40 +859,6 @@ void FIRConverter::genFIR( loc, e3.getResult(0), std::move(conds), op.refs); } -void FIRConverter::genFIR(const Fl::SwitchIOOp &op) {} - -void FIRConverter::genFIR(const Pa::AllocateStmt &stmt) {} -void FIRConverter::genFIR(const Pa::BackspaceStmt &stmt) { - // builder->create(stmt.v); -} -void FIRConverter::genFIR(const Pa::CallStmt &stmt) {} -void FIRConverter::genFIR(const Pa::CloseStmt &stmt) {} -void FIRConverter::genFIR(const Pa::DeallocateStmt &stmt) {} -void FIRConverter::genFIR(const Pa::EndfileStmt &stmt) {} -void FIRConverter::genFIR(const Pa::EventPostStmt &stmt) {} -void FIRConverter::genFIR(const Pa::EventWaitStmt &stmt) {} -void FIRConverter::genFIR(const Pa::FlushStmt &stmt) {} -void FIRConverter::genFIR(const Pa::FormTeamStmt &stmt) {} -void FIRConverter::genFIR(const Pa::InquireStmt &stmt) {} -void FIRConverter::genFIR(const Pa::LockStmt &stmt) {} -void FIRConverter::genFIR(const Pa::NullifyStmt &stmt) {} -void FIRConverter::genFIR(const Pa::OpenStmt &stmt) {} -void FIRConverter::genFIR(const Pa::PointerAssignmentStmt &stmt) {} -void FIRConverter::genFIR(const Pa::PrintStmt &stmt) {} -void FIRConverter::genFIR(const Pa::ReadStmt &stmt) {} -void FIRConverter::genFIR(const Pa::RewindStmt &stmt) {} -void FIRConverter::genFIR(const Pa::SyncAllStmt &stmt) {} -void FIRConverter::genFIR(const Pa::SyncImagesStmt &stmt) {} -void FIRConverter::genFIR(const Pa::SyncMemoryStmt &stmt) {} -void FIRConverter::genFIR(const Pa::SyncTeamStmt &stmt) {} -void FIRConverter::genFIR(const Pa::UnlockStmt &stmt) {} -void FIRConverter::genFIR(const Pa::WaitStmt &stmt) {} -void FIRConverter::genFIR(const Pa::WhereStmt &stmt) {} -void FIRConverter::genFIR(const Pa::WriteStmt &stmt) {} -void FIRConverter::genFIR(const Pa::ForallStmt &stmt) {} -void FIRConverter::genFIR(AnalysisData &ad, const Pa::AssignStmt &stmt) {} -void FIRConverter::genFIR(const Pa::PauseStmt &stmt) {} - /// translate action statements void FIRConverter::genFIR(AnalysisData &ad, const Fl::ActionOp &op) { setCurrentPos(op.v->source); @@ -852,6 +884,7 @@ void FIRConverter::genFIR(AnalysisData &ad, const Fl::ActionOp &op) { void FIRConverter::genFIR(AnalysisData &ad, const Fl::IndirectGotoOp &op) { // add or queue an igoto + TODO(); } void FIRConverter::genFIR(AnalysisData &ad, std::list &operations) { diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index afb398c1abb6..3827a0a37477 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -430,6 +430,7 @@ class ExprLowering { Ev::Expr{Ev::Constant{opt->AIMAG()}}}); } assert(false && "array of complex unhandled"); + return {}; } else { assert(false && "unhandled constant"); return {}; From e8404a11166c82583d526b2a9593c3a88fbe1119 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Fri, 25 Oct 2019 16:09:01 -0700 Subject: [PATCH 019/123] fix bug with overly exuberate merging create Transforms folder clone the MLIR CSE pass for modification --- include/fir/FIROps.td | 9 +- .../fir/Transforms/{MemToReg.h => Passes.h} | 24 +- lib/fir/CMakeLists.txt | 6 +- lib/fir/Dialect.cpp | 9 +- lib/fir/Transforms/CMakeLists.txt | 29 ++ lib/fir/Transforms/CSE.cpp | 266 ++++++++++++++++++ lib/fir/{ => Transforms}/MemToReg.cpp | 4 +- tools/fml/CMakeLists.txt | 1 + tools/fml/fml.cc | 6 +- 9 files changed, 326 insertions(+), 28 deletions(-) rename include/fir/Transforms/{MemToReg.h => Passes.h} (79%) create mode 100644 lib/fir/Transforms/CMakeLists.txt create mode 100644 lib/fir/Transforms/CSE.cpp rename lib/fir/{ => Transforms}/MemToReg.cpp (99%) diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index 641beea160ba..9d7bbcc9ac5d 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -189,15 +189,14 @@ class fir_AllocatableOp traits =[]> : if (!parser.parseOptionalComma()) { llvm::SmallVector typeVec; llvm::SmallVector operands; - if (parser.parseOperandList(operands, - M::OpAsmParser::Delimiter::None) || - parser.parseOptionalAttributeDict(result.attributes) || + if (parser.parseOperandList(operands, M::OpAsmParser::Delimiter::None) || parser.resolveOperands(operands, builder.getIndexType(), parser.getNameLoc(), result.operands)) return M::failure(); } M::Type restype; - if (parser.parseColonType(restype) || + if (parser.parseOptionalAttributeDict(result.attributes) || + parser.parseColonType(restype) || parser.addTypeToList(restype, result.types)) return M::failure(); return M::success(); @@ -248,7 +247,7 @@ def fir_AllocaOp : fir_AllocatableOp<"alloca"> { }]; } -def fir_LoadOp : fir_OneResultOp<"load", [NoSideEffect]>, +def fir_LoadOp : fir_OneResultOp<"load", []>, Arguments<(ins AnyReferenceLike:$memref)> { let summary = "load a value from a memory reference"; diff --git a/include/fir/Transforms/MemToReg.h b/include/fir/Transforms/Passes.h similarity index 79% rename from include/fir/Transforms/MemToReg.h rename to include/fir/Transforms/Passes.h index 67e060ac0bfd..64fd3bebefaf 100644 --- a/include/fir/Transforms/MemToReg.h +++ b/include/fir/Transforms/Passes.h @@ -12,28 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIR_TRANSFORMS_MEMTOREG_H -#define FIR_TRANSFORMS_MEMTOREG_H - -/// A pass to convert the FIR dialect from "Mem-SSA" form to "Reg-SSA" -/// form. This pass is a port of LLVM's mem2reg pass, but modified for the FIR -/// dialect as well as the restructuring of MLIR's representation to present PHI -/// nodes as block arguments. +#ifndef FIR_TRANSFORMS_PASSES_H +#define FIR_TRANSFORMS_PASSES_H #include namespace mlir { +class FuncOp; template class OpPassBase; -class FuncOp; -using FunctionPassBase = OpPassBase; } // namespace mlir namespace fir { -/// Creates a pass to convert FIR into a reg SSA form -std::unique_ptr createMemToRegPass(); +/// Effects aware CSE pass +std::unique_ptr> createCSEPass(); + +/// A pass to convert the FIR dialect from "Mem-SSA" form to "Reg-SSA" +/// form. This pass is a port of LLVM's mem2reg pass, but modified for the FIR +/// dialect as well as the restructuring of MLIR's representation to present PHI +/// nodes as block arguments. +std::unique_ptr> createMemToRegPass(); } // namespace fir -#endif // FIR_TRANSFORMS_MEMTOREG_H +#endif // FIR_TRANSFORMS_PASSES_H diff --git a/lib/fir/CMakeLists.txt b/lib/fir/CMakeLists.txt index 53a827dec1a0..f10d851171b8 100644 --- a/lib/fir/CMakeLists.txt +++ b/lib/fir/CMakeLists.txt @@ -12,13 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -add_library(FIR +add_subdirectory(Transforms) + +add_llvm_library(FIR Attribute.cpp Dialect.cpp FIROps.cpp IteratedDominanceFrontier.cpp KindMapping.cpp - MemToReg.cpp StdConverter.cpp Tilikum.cpp Type.cpp @@ -43,5 +44,4 @@ target_link_libraries(FIR install (TARGETS FIR ARCHIVE DESTINATION lib LIBRARY DESTINATION lib - RUNTIME DESTINATION bin ) diff --git a/lib/fir/Dialect.cpp b/lib/fir/Dialect.cpp index decddd87527c..432686041ced 100644 --- a/lib/fir/Dialect.cpp +++ b/lib/fir/Dialect.cpp @@ -18,6 +18,7 @@ #include "fir/Type.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/IR/StandardTypes.h" +#include "mlir/Transforms/SideEffectsInterface.h" namespace M = mlir; @@ -57,8 +58,10 @@ fir::FIROpsDialect::FIROpsDialect(M::MLIRContext *ctx) >(); } -// anchor the class vtable -fir::FIROpsDialect::~FIROpsDialect() {} +// anchor the class vtable to this compilation unit +fir::FIROpsDialect::~FIROpsDialect() { + // do nothing +} M::Type fir::FIROpsDialect::parseType(llvm::StringRef rawData, M::Location loc) const { @@ -91,6 +94,6 @@ void fir::FIROpsDialect::printAttribute(M::Attribute attr, } else if (attr.dyn_cast_or_null()) { os << fir::UpperBoundAttr::getAttrName(); } else { - assert(false); + assert(false && "attribute pretty-printer is not implemented"); } } diff --git a/lib/fir/Transforms/CMakeLists.txt b/lib/fir/Transforms/CMakeLists.txt new file mode 100644 index 000000000000..a8ddb6d71ba9 --- /dev/null +++ b/lib/fir/Transforms/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_llvm_library(FIRTransforms + CSE.cpp + MemToReg.cpp +) + +add_dependencies(FIRTransforms FIROpsIncGen) + +target_link_libraries(FIRTransforms + FIR +) + +install (TARGETS FIRTransforms + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) diff --git a/lib/fir/Transforms/CSE.cpp b/lib/fir/Transforms/CSE.cpp new file mode 100644 index 000000000000..d5216b003013 --- /dev/null +++ b/lib/fir/Transforms/CSE.cpp @@ -0,0 +1,266 @@ +//===- CSE.cpp - Common Sub-expression Elimination ------------------------===// +// +// Copyright 2019 The MLIR Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= +// +// This transformation pass performs a simple common sub-expression elimination +// algorithm on operations within a function. +// +//===----------------------------------------------------------------------===// + +#include "fir/Transforms/Passes.h" +#include "mlir/Analysis/Dominance.h" +#include "mlir/IR/Attributes.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/Function.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Support/Functional.h" +#include "mlir/Transforms/Passes.h" +#include "mlir/Transforms/Utils.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/ScopedHashTable.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/RecyclingAllocator.h" +#include + +using namespace mlir; + +namespace { +// TODO(riverriddle) Handle commutative operations. +struct SimpleOperationInfo : public llvm::DenseMapInfo { + static unsigned getHashValue(const Operation *opC) { + auto *op = const_cast(opC); + // Hash the operations based upon their: + // - Operation Name + // - Attributes + // - Result Types + // - Operands + return hash_combine( + op->getName(), op->getAttrs(), + hash_combine_range(op->result_type_begin(), op->result_type_end()), + hash_combine_range(op->operand_begin(), op->operand_end())); + } + static bool isEqual(const Operation *lhsC, const Operation *rhsC) { + auto *lhs = const_cast(lhsC); + auto *rhs = const_cast(rhsC); + if (lhs == rhs) + return true; + if (lhs == getTombstoneKey() || lhs == getEmptyKey() || + rhs == getTombstoneKey() || rhs == getEmptyKey()) + return false; + + // Compare the operation name. + if (lhs->getName() != rhs->getName()) + return false; + // Check operand and result type counts. + if (lhs->getNumOperands() != rhs->getNumOperands() || + lhs->getNumResults() != rhs->getNumResults()) + return false; + // Compare attributes. + if (lhs->getAttrs() != rhs->getAttrs()) + return false; + // Compare operands. + if (!std::equal(lhs->operand_begin(), lhs->operand_end(), + rhs->operand_begin())) + return false; + // Compare result types. + return std::equal(lhs->result_type_begin(), lhs->result_type_end(), + rhs->result_type_begin()); + } +}; + +/// Simple common sub-expression elimination. +struct CSE : public FunctionPass { + CSE() = default; + CSE(const CSE &) {} + + /// Shared implementation of operation elimination and scoped map definitions. + using AllocatorTy = llvm::RecyclingAllocator< + llvm::BumpPtrAllocator, + llvm::ScopedHashTableVal>; + using ScopedMapTy = llvm::ScopedHashTable; + + /// Represents a single entry in the depth first traversal of a CFG. + struct CFGStackNode { + CFGStackNode(ScopedMapTy &knownValues, DominanceInfoNode *node) + : scope(knownValues), node(node), childIterator(node->begin()), + processed(false) {} + + /// Scope for the known values. + ScopedMapTy::ScopeTy scope; + + DominanceInfoNode *node; + DominanceInfoNode::iterator childIterator; + + /// If this node has been fully processed yet or not. + bool processed; + }; + + /// Attempt to eliminate a redundant operation. Returns success if the + /// operation was marked for removal, failure otherwise. + LogicalResult simplifyOperation(ScopedMapTy &knownValues, Operation *op); + + void simplifyBlock(ScopedMapTy &knownValues, DominanceInfo &domInfo, + Block *bb); + void simplifyRegion(ScopedMapTy &knownValues, DominanceInfo &domInfo, + Region ®ion); + + void runOnFunction() override; + +private: + /// Operations marked as dead and to be erased. + std::vector opsToErase; +}; + +/// Attempt to eliminate a redundant operation. +LogicalResult CSE::simplifyOperation(ScopedMapTy &knownValues, Operation *op) { + // Don't simplify operations with nested blocks. We don't currently model + // equality comparisons correctly among other things. It is also unclear + // whether we would want to CSE such operations. + if (op->getNumRegions() != 0) + return failure(); + + // TODO(riverriddle) We currently only eliminate non side-effecting + // operations. + if (!op->hasNoSideEffect()) + return failure(); + + // If the operation is already trivially dead just add it to the erase list. + if (op->use_empty()) { + opsToErase.push_back(op); + return success(); + } + + // Look for an existing definition for the operation. + if (auto *existing = knownValues.lookup(op)) { + // If we find one then replace all uses of the current operation with the + // existing one and mark it for deletion. + op->replaceAllUsesWith(existing); + opsToErase.push_back(op); + + // If the existing operation has an unknown location and the current + // operation doesn't, then set the existing op's location to that of the + // current op. + if (existing->getLoc().isa() && + !op->getLoc().isa()) { + existing->setLoc(op->getLoc()); + } + return success(); + } + + // Otherwise, we add this operation to the known values map. + knownValues.insert(op, op); + return failure(); +} + +void CSE::simplifyBlock(ScopedMapTy &knownValues, DominanceInfo &domInfo, + Block *bb) { + for (auto &inst : *bb) { + // If the operation is simplified, we don't process any held regions. + if (succeeded(simplifyOperation(knownValues, &inst))) + continue; + + // If this operation is isolated above, we can't process nested regions with + // the given 'knownValues' map. This would cause the insertion of implicit + // captures in explicit capture only regions. + if (!inst.isRegistered() || inst.isKnownIsolatedFromAbove()) { + ScopedMapTy nestedKnownValues; + for (auto ®ion : inst.getRegions()) + simplifyRegion(nestedKnownValues, domInfo, region); + continue; + } + + // Otherwise, process nested regions normally. + for (auto ®ion : inst.getRegions()) + simplifyRegion(knownValues, domInfo, region); + } +} + +void CSE::simplifyRegion(ScopedMapTy &knownValues, DominanceInfo &domInfo, + Region ®ion) { + // If the region is empty there is nothing to do. + if (region.empty()) + return; + + // If the region only contains one block, then simplify it directly. + if (std::next(region.begin()) == region.end()) { + ScopedMapTy::ScopeTy scope(knownValues); + simplifyBlock(knownValues, domInfo, ®ion.front()); + return; + } + + // Note, deque is being used here because there was significant performance + // gains over vector when the container becomes very large due to the + // specific access patterns. If/when these performance issues are no + // longer a problem we can change this to vector. For more information see + // the llvm mailing list discussion on this: + // http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20120116/135228.html + std::deque> stack; + + // Process the nodes of the dom tree for this region. + stack.emplace_back(std::make_unique( + knownValues, domInfo.getRootNode(®ion))); + + while (!stack.empty()) { + auto ¤tNode = stack.back(); + + // Check to see if we need to process this node. + if (!currentNode->processed) { + currentNode->processed = true; + simplifyBlock(knownValues, domInfo, currentNode->node->getBlock()); + } + + // Otherwise, check to see if we need to process a child node. + if (currentNode->childIterator != currentNode->node->end()) { + auto *childNode = *(currentNode->childIterator++); + stack.emplace_back( + std::make_unique(knownValues, childNode)); + } else { + // Finally, if the node and all of its children have been processed + // then we delete the node. + stack.pop_back(); + } + } +} + +void CSE::runOnFunction() { + /// A scoped hash table of defining operations within a function. + ScopedMapTy knownValues; + simplifyRegion(knownValues, getAnalysis(), + getFunction().getBody()); + + // If no operations were erased, then we mark all analyses as preserved. + if (opsToErase.empty()) + return ;//markAllAnalysesPreserved(); + + /// Erase any operations that were marked as dead during simplification. + for (auto *op : opsToErase) + op->erase(); + opsToErase.clear(); + + // We currently don't remove region operations, so mark dominance as + // preserved. + //markAnalysesPreserved(); +} +} // end anonymous namespace + +std::unique_ptr> fir::createCSEPass() { + return std::make_unique(); +} + +static PassRegistration + pass("cse", "Eliminate common sub-expressions in functions"); diff --git a/lib/fir/MemToReg.cpp b/lib/fir/Transforms/MemToReg.cpp similarity index 99% rename from lib/fir/MemToReg.cpp rename to lib/fir/Transforms/MemToReg.cpp index 76aafbbe8715..2104f8ddf97c 100644 --- a/lib/fir/MemToReg.cpp +++ b/lib/fir/Transforms/MemToReg.cpp @@ -6,10 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "fir/Transforms/MemToReg.h" #include "fir/Analysis/IteratedDominanceFrontier.h" #include "fir/Dialect.h" #include "fir/FIROps.h" +#include "fir/Transforms/Passes.h" #include "mlir/Analysis/Dominance.h" #include "mlir/Dialect/StandardOps/Ops.h" #include "mlir/Pass/Pass.h" @@ -735,6 +735,6 @@ struct MemToReg : public M::FunctionPass> { } // namespace -std::unique_ptr fir::createMemToRegPass() { +std::unique_ptr> fir::createMemToRegPass() { return std::make_unique>(); } diff --git a/tools/fml/CMakeLists.txt b/tools/fml/CMakeLists.txt index c30dcdc5dcab..1f79d37d0233 100644 --- a/tools/fml/CMakeLists.txt +++ b/tools/fml/CMakeLists.txt @@ -32,6 +32,7 @@ target_link_libraries(fml FortranEvaluate FortranSemantics FortranBurnside + FIRTransforms ${LIBS} ) diff --git a/tools/fml/fml.cc b/tools/fml/fml.cc index fd8019480793..de7af03a4978 100644 --- a/tools/fml/fml.cc +++ b/tools/fml/fml.cc @@ -16,7 +16,7 @@ #include "fir/Dialect.h" #include "fir/Tilikum/Tilikum.h" -#include "fir/Transforms/MemToReg.h" +#include "fir/Transforms/Passes.h" #include "fir/Transforms/StdConverter.h" #include "../../lib/burnside/bridge.h" #include "../../lib/burnside/convert-expr.h" @@ -276,9 +276,9 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, llvm::errs() << ";== 1 ==\n"; mlirModule.dump(); } + // Run FIR mem2reg and CSE as a pair pm.addPass(fir::createMemToRegPass()); - // Run FIR lowering and CSE as a pair - pm.addPass(mlir::createCSEPass()); + pm.addPass(fir::createCSEPass()); if (driver.lowerToStd) { pm.addPass(fir::createFIRToStdPass()); } From 9f3925afe8d512e9b5cff8467708382ff348114d Mon Sep 17 00:00:00 2001 From: Eric Date: Sat, 26 Oct 2019 17:30:10 -0700 Subject: [PATCH 020/123] implementation of basic CSE --- include/fir/FIROps.h | 20 +++++++ lib/fir/Transforms/CSE.cpp | 105 ++++++++++++++++++++++++++++--------- test/fir/commute.fir | 21 ++++++++ tools/fml/fml.cc | 4 +- 4 files changed, 123 insertions(+), 27 deletions(-) create mode 100644 test/fir/commute.fir diff --git a/include/fir/FIROps.h b/include/fir/FIROps.h index 3a9bbf99af04..ef6532c9126f 100644 --- a/include/fir/FIROps.h +++ b/include/fir/FIROps.h @@ -102,6 +102,26 @@ mlir::ParseResult parseSelector(mlir::OpAsmParser *parser, LoopOp getForInductionVarOwner(mlir::Value *val); +/// return true iff the Operation is a non-volatile LoadOp +inline bool nonVolatileLoad(mlir::Operation *op) { + if (auto load = dyn_cast(op)) + if (!load.getAttr("volatile")) + return true; + return false; +} + +/// return true iff the Operation is a CallOp or DispatchOp and not pure +inline bool impureCall(mlir::Operation *op) { + if (auto call = dyn_cast(op)) { + if (!call.getAttr("pure")) + return true; + } else if (auto dispatch = dyn_cast(op)) { + if (!dispatch.getAttr("pure")) + return true; + } + return false; +} + } // namespace fir #endif // FIR_FIROPS_H diff --git a/lib/fir/Transforms/CSE.cpp b/lib/fir/Transforms/CSE.cpp index d5216b003013..45dff9d123c2 100644 --- a/lib/fir/Transforms/CSE.cpp +++ b/lib/fir/Transforms/CSE.cpp @@ -20,6 +20,7 @@ // //===----------------------------------------------------------------------===// +#include "fir/FIROps.h" #include "fir/Transforms/Passes.h" #include "mlir/Analysis/Dominance.h" #include "mlir/IR/Attributes.h" @@ -33,13 +34,19 @@ #include "llvm/ADT/Hashing.h" #include "llvm/ADT/ScopedHashTable.h" #include "llvm/Support/Allocator.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/RecyclingAllocator.h" #include using namespace mlir; +static llvm::cl::opt + ClLeaveEffects("keep-effects", + llvm::cl::desc("disable cleaning up effects attributes"), + llvm::cl::init(false), llvm::cl::Hidden); + namespace { -// TODO(riverriddle) Handle commutative operations. + struct SimpleOperationInfo : public llvm::DenseMapInfo { static unsigned getHashValue(const Operation *opC) { auto *op = const_cast(opC); @@ -48,11 +55,20 @@ struct SimpleOperationInfo : public llvm::DenseMapInfo { // - Attributes // - Result Types // - Operands + unsigned hashOps; + if (op->isCommutative()) { + std::vector vec(op->operand_begin(), op->operand_end()); + llvm::sort(vec.begin(), vec.end()); + hashOps = llvm::hash_combine_range(vec.begin(), vec.end()); + } else { + hashOps = hash_combine_range(op->operand_begin(), op->operand_end()); + } return hash_combine( op->getName(), op->getAttrs(), hash_combine_range(op->result_type_begin(), op->result_type_end()), - hash_combine_range(op->operand_begin(), op->operand_end())); + hashOps); } + static bool isEqual(const Operation *lhsC, const Operation *rhsC) { auto *lhs = const_cast(lhsC); auto *rhs = const_cast(rhsC); @@ -73,19 +89,28 @@ struct SimpleOperationInfo : public llvm::DenseMapInfo { if (lhs->getAttrs() != rhs->getAttrs()) return false; // Compare operands. - if (!std::equal(lhs->operand_begin(), lhs->operand_end(), - rhs->operand_begin())) - return false; + if (lhs->isCommutative()) { + SmallVector lops(lhs->operand_begin(), lhs->operand_end()); + llvm::sort(lops.begin(), lops.end()); + SmallVector rops(rhs->operand_begin(), rhs->operand_end()); + llvm::sort(rops.begin(), rops.end()); + if (!std::equal(lops.begin(), lops.end(), rops.begin())) + return false; + } else { + if (!std::equal(lhs->operand_begin(), lhs->operand_end(), + rhs->operand_begin())) + return false; + } // Compare result types. return std::equal(lhs->result_type_begin(), lhs->result_type_end(), rhs->result_type_begin()); } }; -/// Simple common sub-expression elimination. -struct CSE : public FunctionPass { - CSE() = default; - CSE(const CSE &) {} +/// Basic common sub-expression elimination. +struct BasicCSE : public FunctionPass { + BasicCSE() = default; + BasicCSE(const BasicCSE &) {} /// Shared implementation of operation elimination and scoped map definitions. using AllocatorTy = llvm::RecyclingAllocator< @@ -118,6 +143,21 @@ struct CSE : public FunctionPass { Block *bb); void simplifyRegion(ScopedMapTy &knownValues, DominanceInfo &domInfo, Region ®ion); + + void cleanupBlock(Block *bb) { + for (auto &inst : *bb) { + if (fir::nonVolatileLoad(&inst)) { + inst.removeAttr(Identifier::get("effects_token", inst.getContext())); + } else if (inst.getNumRegions()) { + for (auto ®ion : inst.getRegions()) + cleanupRegion(region); + } + } + } + void cleanupRegion(Region ®ion) { + for (auto &block : region) + cleanupBlock(&block); + } void runOnFunction() override; @@ -127,16 +167,15 @@ struct CSE : public FunctionPass { }; /// Attempt to eliminate a redundant operation. -LogicalResult CSE::simplifyOperation(ScopedMapTy &knownValues, Operation *op) { +LogicalResult BasicCSE::simplifyOperation(ScopedMapTy &knownValues, + Operation *op) { // Don't simplify operations with nested blocks. We don't currently model // equality comparisons correctly among other things. It is also unclear // whether we would want to CSE such operations. if (op->getNumRegions() != 0) return failure(); - // TODO(riverriddle) We currently only eliminate non side-effecting - // operations. - if (!op->hasNoSideEffect()) + if (!op->hasNoSideEffect() && !fir::nonVolatileLoad(op)) return failure(); // If the operation is already trivially dead just add it to the erase list. @@ -167,8 +206,16 @@ LogicalResult CSE::simplifyOperation(ScopedMapTy &knownValues, Operation *op) { return failure(); } -void CSE::simplifyBlock(ScopedMapTy &knownValues, DominanceInfo &domInfo, - Block *bb) { +void BasicCSE::simplifyBlock(ScopedMapTy &knownValues, DominanceInfo &domInfo, + Block *bb) { + std::intptr_t token = reinterpret_cast(bb); + for (auto &inst : *bb) { + if (fir::nonVolatileLoad(&inst)) + inst.setAttr("effects_token", + IntegerAttr::get(IndexType::get(inst.getContext()), token)); + if (dyn_cast(&inst) || fir::impureCall(&inst)) + token = reinterpret_cast(&inst); + } for (auto &inst : *bb) { // If the operation is simplified, we don't process any held regions. if (succeeded(simplifyOperation(knownValues, &inst))) @@ -190,8 +237,8 @@ void CSE::simplifyBlock(ScopedMapTy &knownValues, DominanceInfo &domInfo, } } -void CSE::simplifyRegion(ScopedMapTy &knownValues, DominanceInfo &domInfo, - Region ®ion) { +void BasicCSE::simplifyRegion(ScopedMapTy &knownValues, DominanceInfo &domInfo, + Region ®ion) { // If the region is empty there is nothing to do. if (region.empty()) return; @@ -237,15 +284,20 @@ void CSE::simplifyRegion(ScopedMapTy &knownValues, DominanceInfo &domInfo, } } -void CSE::runOnFunction() { +void BasicCSE::runOnFunction() { /// A scoped hash table of defining operations within a function. - ScopedMapTy knownValues; - simplifyRegion(knownValues, getAnalysis(), - getFunction().getBody()); + { + ScopedMapTy knownValues; + simplifyRegion(knownValues, getAnalysis(), + getFunction().getBody()); + } + if (!ClLeaveEffects) { + cleanupRegion(getFunction().getBody()); + } // If no operations were erased, then we mark all analyses as preserved. if (opsToErase.empty()) - return ;//markAllAnalysesPreserved(); + return markAllAnalysesPreserved(); /// Erase any operations that were marked as dead during simplification. for (auto *op : opsToErase) @@ -254,13 +306,14 @@ void CSE::runOnFunction() { // We currently don't remove region operations, so mark dominance as // preserved. - //markAnalysesPreserved(); + markAnalysesPreserved(); } + } // end anonymous namespace std::unique_ptr> fir::createCSEPass() { - return std::make_unique(); + return std::make_unique(); } -static PassRegistration - pass("cse", "Eliminate common sub-expressions in functions"); +static PassRegistration + pass("basiccse", "Eliminate common sub-expressions in functions"); diff --git a/test/fir/commute.fir b/test/fir/commute.fir new file mode 100644 index 000000000000..6e55bb0e36be --- /dev/null +++ b/test/fir/commute.fir @@ -0,0 +1,21 @@ +func @f1(%a : i32, %b : i32) -> i32 { + %1 = addi %a, %b : i32 + %2 = addi %b, %a : i32 + %3 = muli %1, %2 : i32 + return %3 : i32 +} + +func @f2(%a : !fir.ref) -> i32 { + %1 = fir.load %a : !fir.ref + %2 = fir.load %a : !fir.ref + %3 = addi %1, %2 : i32 + %4 = fir.load %a : !fir.ref + %5 = addi %3, %4 : i32 + %6 = fir.load %a : !fir.ref + %7 = addi %5, %6 : i32 + %8 = fir.load %a : !fir.ref + %9 = addi %7, %8 : i32 + %10 = fir.load %a : !fir.ref + %11 = addi %10, %9 : i32 + return %11 : i32 +} diff --git a/tools/fml/fml.cc b/tools/fml/fml.cc index de7af03a4978..a09a5629f4b9 100644 --- a/tools/fml/fml.cc +++ b/tools/fml/fml.cc @@ -330,7 +330,7 @@ std::string CompileFir(std::string path, Fortran::parser::Options options, // run passes mlir::PassManager pm{mlirModule.getContext()}; pm.addPass(fir::createMemToRegPass()); - pm.addPass(mlir::createCSEPass()); + pm.addPass(fir::createCSEPass()); if (driver.lowerToStd) { pm.addPass(fir::createFIRToStdPass()); } @@ -604,6 +604,8 @@ int main(int argc, char *const argv[]) { .set_searchDirectories(driver.searchDirectories) .set_warnOnNonstandardUsage(driver.warnOnNonstandardUsage) .set_warningsAreErrors(driver.warningsAreErrors); + + //llvm::cl::ParseCommandLineOptions(argc, argv, "Fortran/FIR/MLIR compiler\n"); if (!anyFiles) { driver.measureTree = true; From f8fe621d39e2de1e046c3959ec201415efd4f77d Mon Sep 17 00:00:00 2001 From: Eric Date: Sun, 27 Oct 2019 09:51:18 -0700 Subject: [PATCH 021/123] fix option parsing --- tools/fml/fml.cc | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tools/fml/fml.cc b/tools/fml/fml.cc index a09a5629f4b9..05293d6f54c1 100644 --- a/tools/fml/fml.cc +++ b/tools/fml/fml.cc @@ -588,12 +588,17 @@ int main(int argc, char *const argv[]) { if (driver.warnOnNonstandardUsage) { options.features.WarnOnAllNonstandard(); } - if (!options.features.IsEnabled( - Fortran::parser::LanguageFeature::BackslashEscapes)) { - driver.pgf90Args.push_back("-Mbackslash"); - } - if (options.features.IsEnabled(Fortran::parser::LanguageFeature::OpenMP)) { - driver.pgf90Args.push_back("-mp"); + + if (driver.pgf90Args.size() > 0) { + // marshal the arguments for cl parsing + int argc_cp = driver.pgf90Args.size(); + const char **argv_cp = new const char *[argc_cp + 1]; + for (int i = 0; i < argc_cp; ++i) + argv_cp[i] = driver.pgf90Args[i].c_str(); + argv_cp[argc_cp] = nullptr; + llvm::cl::ParseCommandLineOptions( + argc_cp, argv_cp, "Fortran/FIR/MLIR compiler\n"); + delete[] argv_cp; } Fortran::parser::AllSources allSources; @@ -604,8 +609,6 @@ int main(int argc, char *const argv[]) { .set_searchDirectories(driver.searchDirectories) .set_warnOnNonstandardUsage(driver.warnOnNonstandardUsage) .set_warningsAreErrors(driver.warningsAreErrors); - - //llvm::cl::ParseCommandLineOptions(argc, argv, "Fortran/FIR/MLIR compiler\n"); if (!anyFiles) { driver.measureTree = true; From 16054e9d1164dc5e91335e687336e4b39d428506 Mon Sep 17 00:00:00 2001 From: Eric Date: Sun, 27 Oct 2019 12:39:42 -0700 Subject: [PATCH 022/123] Expand genEnterFIR, FORALL construct, etc. --- lib/burnside/bridge.cc | 166 +++++++++++++++++++---------------------- 1 file changed, 76 insertions(+), 90 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index c68970f49551..22527ae4e055 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -177,6 +177,22 @@ class FIRConverter { return build().create(lhs->getLoc(), lhs, rhs); } + template void genFIR(const Co::Indirection &indirection) { + genFIR(indirection.value()); + } + template void genFIR(const Pa::Statement &stmt) { + setCurrentPos(stmt.source); + genFIR(stmt.statement); + } + template void genFIR(const std::list &list) { + for (auto &a : list) { + genFIR(a); + } + } + template void genFIROnVariant(const A &variant) { + std::visit([&](auto &s) { genFIR(s); }, variant.u); + } + void genFIR(AnalysisData &ad, std::list &operations); // Control flow destination @@ -335,14 +351,42 @@ class FIRConverter { pushDoContext(&ss); } } - template void genEnterFIR(const A &construct) { - // FIXME: add other genEnterFIR() members and delete this stub + + /// Lower FORALL construct (See 10.2.4) + void genEnterFIR(const Pa::ForallConstruct &forall) { + auto &stmt{std::get>(forall.t)}; + setCurrentPos(stmt.source); + auto &fas{stmt.statement}; + auto &ctrl{std::get>(fas.t).value()}; + auto &bld{build()}; + // bld.create(); + for (auto &s : std::get>(forall.t)) { + genFIROnVariant(s); + } TODO(); } + void genFIR(const Pa::ForallConstruct &forall) { genEnterFIR(forall); } + void genFIR(const Pa::ForallAssignmentStmt &s) { genFIROnVariant(s); } + + void genEnterFIR(const Pa::WhereConstruct &where) { TODO(); } + void genFIR(const Pa::WhereConstruct &where) { genEnterFIR(where); } + void genFIR(const Fl::BeginOp &op) { std::visit([&](auto *construct) { genEnterFIR(*construct); }, op.u); } + void genEnterFIR(const Pa::AssociateConstruct &) { TODO(); } + void genEnterFIR(const Pa::BlockConstruct &) { TODO(); } + void genEnterFIR(const Pa::CaseConstruct &) { TODO(); } + void genEnterFIR(const Pa::ChangeTeamConstruct &) { TODO(); } + void genEnterFIR(const Pa::CriticalConstruct &) { TODO(); } + void genEnterFIR(const Pa::IfConstruct &) { TODO(); } + void genEnterFIR(const Pa::CompilerDirective &) { TODO(); } + void genEnterFIR(const Pa::OpenMPConstruct &) { TODO(); } + void genEnterFIR(const Pa::OmpEndLoopDirective &) { TODO(); } + void genEnterFIR(const Pa::SelectRankConstruct &) { TODO(); } + void genEnterFIR(const Pa::SelectTypeConstruct &) { TODO(); } + void genExitFIR(const Pa::DoConstruct &construct) { if (firLoopOp) { build().setInsertionPointAfter( @@ -500,9 +544,7 @@ class FIRConverter { } // Action statements - void genFIR(const Pa::AllocateStmt &stmt) { - TODO(); - } + void genFIR(const Pa::AllocateStmt &stmt) { TODO(); } void genFIR(const Pa::AssignmentStmt &stmt) { auto *rhs{Se::GetExpr(std::get(stmt.t))}; auto *lhs{Se::GetExpr(std::get(stmt.t))}; @@ -510,90 +552,34 @@ class FIRConverter { build().create( loc, createFIRExpr(loc, rhs), createFIRAddr(loc, lhs)); } - void genFIR(const Pa::BackspaceStmt &stmt) { - TODO(); - } - void genFIR(const Pa::CallStmt &stmt) { - TODO(); - } - void genFIR(const Pa::CloseStmt &stmt) { - TODO(); - } - void genFIR(const Pa::DeallocateStmt &stmt) { - TODO(); - } - void genFIR(const Pa::EndfileStmt &stmt) { - TODO(); - } - void genFIR(const Pa::EventPostStmt &stmt) { - TODO(); - } - void genFIR(const Pa::EventWaitStmt &stmt) { - TODO(); - } - void genFIR(const Pa::FlushStmt &stmt) { - TODO(); - } - void genFIR(const Pa::FormTeamStmt &stmt){ - TODO(); - } - void genFIR(const Pa::InquireStmt &stmt) { - TODO(); - } - void genFIR(const Pa::LockStmt &stmt) { - TODO(); - } - void genFIR(const Pa::NullifyStmt &stmt) { - TODO(); - } - void genFIR(const Pa::OpenStmt &stmt) { - TODO(); - } - void genFIR(const Pa::PointerAssignmentStmt &stmt){ - TODO(); - } - void genFIR(const Pa::PrintStmt &stmt){ - TODO(); - } - void genFIR(const Pa::ReadStmt &stmt){ - TODO(); - } - void genFIR(const Pa::RewindStmt &stmt) { - TODO(); - } - void genFIR(const Pa::SyncAllStmt &stmt){ - TODO(); - } - void genFIR(const Pa::SyncImagesStmt &stmt){ - TODO(); - } - void genFIR(const Pa::SyncMemoryStmt &stmt){ - TODO(); - } - void genFIR(const Pa::SyncTeamStmt &stmt) { - TODO(); - } - void genFIR(const Pa::UnlockStmt &stmt) { - TODO(); - } - void genFIR(const Pa::WaitStmt &stmt){ - TODO(); - } - void genFIR(const Pa::WhereStmt &stmt){ - TODO(); - } - void genFIR(const Pa::WriteStmt &stmt) { - TODO(); - } - void genFIR(const Pa::ForallStmt &stmt) { - TODO(); - } - void genFIR(AnalysisData &ad, const Pa::AssignStmt &stmt) { - TODO(); - } - void genFIR(const Pa::PauseStmt &stmt) { - TODO(); - } + void genFIR(const Pa::BackspaceStmt &stmt) { TODO(); } + void genFIR(const Pa::CallStmt &stmt) { TODO(); } + void genFIR(const Pa::CloseStmt &stmt) { TODO(); } + void genFIR(const Pa::DeallocateStmt &stmt) { TODO(); } + void genFIR(const Pa::EndfileStmt &stmt) { TODO(); } + void genFIR(const Pa::EventPostStmt &stmt) { TODO(); } + void genFIR(const Pa::EventWaitStmt &stmt) { TODO(); } + void genFIR(const Pa::FlushStmt &stmt) { TODO(); } + void genFIR(const Pa::FormTeamStmt &stmt) { TODO(); } + void genFIR(const Pa::InquireStmt &stmt) { TODO(); } + void genFIR(const Pa::LockStmt &stmt) { TODO(); } + void genFIR(const Pa::NullifyStmt &stmt) { TODO(); } + void genFIR(const Pa::OpenStmt &stmt) { TODO(); } + void genFIR(const Pa::PointerAssignmentStmt &stmt) { TODO(); } + void genFIR(const Pa::PrintStmt &stmt) { TODO(); } + void genFIR(const Pa::ReadStmt &stmt) { TODO(); } + void genFIR(const Pa::RewindStmt &stmt) { TODO(); } + void genFIR(const Pa::SyncAllStmt &stmt) { TODO(); } + void genFIR(const Pa::SyncImagesStmt &stmt) { TODO(); } + void genFIR(const Pa::SyncMemoryStmt &stmt) { TODO(); } + void genFIR(const Pa::SyncTeamStmt &stmt) { TODO(); } + void genFIR(const Pa::UnlockStmt &stmt) { TODO(); } + void genFIR(const Pa::WaitStmt &stmt) { TODO(); } + void genFIR(const Pa::WhereStmt &stmt) { TODO(); } + void genFIR(const Pa::WriteStmt &stmt) { TODO(); } + void genFIR(const Pa::ForallStmt &stmt) { TODO(); } + void genFIR(AnalysisData &ad, const Pa::AssignStmt &stmt) { TODO(); } + void genFIR(const Pa::PauseStmt &stmt) { TODO(); } template void translateRoutine( @@ -877,7 +863,7 @@ void FIRConverter::genFIR(AnalysisData &ad, const Fl::ActionOp &op) { [&](const Co::Indirection &assign) { genFIR(ad, assign.value()); }, - [&](const auto &stmt) { genFIR(stmt.value()); }, + [&](const auto &stmt) { genFIR(stmt); }, }, op.v->statement.u); } From 8a767eaaa2fd1d453d4bfe8e1e1a0a1125a1eb36 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Mon, 28 Oct 2019 09:35:35 -0700 Subject: [PATCH 023/123] add support for standard calls --- include/fir/FIROps.h | 20 -------------- include/fir/FIROpsSupport.h | 54 +++++++++++++++++++++++++++++++++++++ lib/fir/Transforms/CSE.cpp | 4 +-- 3 files changed, 56 insertions(+), 22 deletions(-) create mode 100644 include/fir/FIROpsSupport.h diff --git a/include/fir/FIROps.h b/include/fir/FIROps.h index ef6532c9126f..3a9bbf99af04 100644 --- a/include/fir/FIROps.h +++ b/include/fir/FIROps.h @@ -102,26 +102,6 @@ mlir::ParseResult parseSelector(mlir::OpAsmParser *parser, LoopOp getForInductionVarOwner(mlir::Value *val); -/// return true iff the Operation is a non-volatile LoadOp -inline bool nonVolatileLoad(mlir::Operation *op) { - if (auto load = dyn_cast(op)) - if (!load.getAttr("volatile")) - return true; - return false; -} - -/// return true iff the Operation is a CallOp or DispatchOp and not pure -inline bool impureCall(mlir::Operation *op) { - if (auto call = dyn_cast(op)) { - if (!call.getAttr("pure")) - return true; - } else if (auto dispatch = dyn_cast(op)) { - if (!dispatch.getAttr("pure")) - return true; - } - return false; -} - } // namespace fir #endif // FIR_FIROPS_H diff --git a/include/fir/FIROpsSupport.h b/include/fir/FIROpsSupport.h new file mode 100644 index 000000000000..ee7c6b4cb85b --- /dev/null +++ b/include/fir/FIROpsSupport.h @@ -0,0 +1,54 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIR_FIROPSSUPPORT_H +#define FIR_FIROPSSUPPORT_H + +#include "fir/FIROps.h" +#include "mlir/Dialect/StandardOps/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)) + if (!load.getAttr("volatile")) + return true; + return false; +} + +/// return true iff the Operation is a fir::CallOp, fir::DispatchOp, +/// mlir::CallOp, or mlir::CallIndirectOp and not pure +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)) { + if (!call.getAttr("pure")) + return true; + } else if (auto dispatch = dyn_cast(op)) { + if (!dispatch.getAttr("pure")) + return true; + } else if (auto call = dyn_cast(op)) { + if (!call.getAttr("pure")) + return true; + } else if (auto icall = dyn_cast(op)) { + if (!icall.getAttr("pure")) + return true; + } + return false; +} + +} // namespace fir + +#endif // FIR_FIROPSSUPPORT_H diff --git a/lib/fir/Transforms/CSE.cpp b/lib/fir/Transforms/CSE.cpp index 45dff9d123c2..3092f6a61eca 100644 --- a/lib/fir/Transforms/CSE.cpp +++ b/lib/fir/Transforms/CSE.cpp @@ -20,7 +20,7 @@ // //===----------------------------------------------------------------------===// -#include "fir/FIROps.h" +#include "fir/FIROpsSupport.h" #include "fir/Transforms/Passes.h" #include "mlir/Analysis/Dominance.h" #include "mlir/IR/Attributes.h" @@ -143,7 +143,7 @@ struct BasicCSE : public FunctionPass { Block *bb); void simplifyRegion(ScopedMapTy &knownValues, DominanceInfo &domInfo, Region ®ion); - + void cleanupBlock(Block *bb) { for (auto &inst : *bb) { if (fir::nonVolatileLoad(&inst)) { From 32778f433c788a2793fb7b69617b7a871796eda3 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Thu, 24 Oct 2019 07:27:58 -0700 Subject: [PATCH 024/123] More Complex expresison lowering: * Provide a class to help generating fir to manipulate complex in fir. It hides InsertValueOp and ExtractValueOp details. * Implement complex compare * Replace all mlir float basic ops (e.g. mlir::AddFOp) by their FIR equivalent. This is because mlir::AddFOp and such do not support fir real types operands and we cannot ensure all real types are lowered to mlir real types (e.g. fir.real<16> currently). Fir real ops support both fir and mlir real operands. * Prevent re-loading of a same symbol while lowering a same symbol. This especially important for complex because the expression analysis may have already re-written some complex ops as real ops piece-wise. This increase the occurance of symbols. Fortran gives us the guarantee we should not have to reload a var while evaluating an expression (see F2018 section 10.1.4). --- lib/burnside/complex.h | 109 +++++++++++++++++++++++++++++++++++ lib/burnside/convert-expr.cc | 57 +++++++++++------- lib/burnside/intrinsics.cc | 39 ++++++------- 3 files changed, 161 insertions(+), 44 deletions(-) create mode 100644 lib/burnside/complex.h diff --git a/lib/burnside/complex.h b/lib/burnside/complex.h new file mode 100644 index 000000000000..bdf874d6fbca --- /dev/null +++ b/lib/burnside/complex.h @@ -0,0 +1,109 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_COMPLEX_H_ +#define FORTRAN_BURNSIDE_COMPLEX_H_ + +/// [Coding style](https://llvm.org/docs/CodingStandards.html) + +#include "fe-helper.h" +#include "fir/FIROps.h" +#include "fir/Type.h" + +namespace Fortran::burnside { +/// Provide helpers to generate Complex manipulations in FIR. + +class ComplexHandler { +public: + ComplexHandler(mlir::OpBuilder &b, mlir::Location l) : builder{b}, loc{l} {} + mlir::Type getComplexPartType(fir::KindTy complexKind) { + return convertReal(builder.getContext(), complexKind); + } + + mlir::Type getComplexPartType(mlir::Type complexType) { + return getComplexPartType(complexType.cast().getFKind()); + } + + mlir::Type getComplexPartType(mlir::Value *cplx) { + assert(cplx != nullptr); + return getComplexPartType(cplx->getType()); + } + + mlir::Value *createComplexPart(mlir::Value *cplx, bool isImaginaryPart) { + return builder.create( + loc, getComplexPartType(cplx), cplx, getPartId(isImaginaryPart)); + } + + mlir::Value *createComplexRealPart(mlir::Value *cplx) { + return createComplexPart(cplx, false); + } + + mlir::Value *createComplexImagPart(mlir::Value *cplx) { + return createComplexPart(cplx, true); + } + + mlir::Value *setComplexPart( + mlir::Value *cplx, mlir::Value *part, bool isImaginaryPart) { + assert(cplx != nullptr); + return builder.create( + loc, cplx->getType(), cplx, part, getPartId(isImaginaryPart)); + } + mlir::Value *setRealPart(mlir::Value *cplx, mlir::Value *part) { + return setComplexPart(cplx, part, false); + } + mlir::Value *setImagPart(mlir::Value *cplx, mlir::Value *part) { + return setComplexPart(cplx, part, true); + } + + mlir::Value *createComplex( + fir::KindTy kind, mlir::Value *real, mlir::Value *imag) { + mlir::Type complexTy{fir::CplxType::get(builder.getContext(), kind)}; + mlir::Value *und{builder.create(loc, complexTy)}; + return setImagPart(setRealPart(und, real), imag); + } + + mlir::Value *createComplexCompare( + mlir::Value *cplx1, mlir::Value *cplx2, bool eq) { + mlir::Value *real1{createComplexRealPart(cplx1)}; + mlir::Value *real2{createComplexRealPart(cplx2)}; + mlir::Value *imag1{createComplexImagPart(cplx1)}; + mlir::Value *imag2{createComplexImagPart(cplx2)}; + + mlir::CmpFPredicate predicate{ + eq ? mlir::CmpFPredicate::UEQ : mlir::CmpFPredicate::UNE}; + mlir::Value *realCmp{ + builder.create(loc, predicate, real1, real2).getResult()}; + mlir::Value *imagCmp{ + builder.create(loc, predicate, imag1, imag2).getResult()}; + + if (eq) { + return builder.create(loc, realCmp, imagCmp).getResult(); + } else { + return builder.create(loc, realCmp, imagCmp).getResult(); + } + } + +private: + inline mlir::Value *getPartId(bool isImaginaryPart) { + auto type{mlir::IntegerType::get(32, builder.getContext())}; + auto attr{builder.getIntegerAttr(type, isImaginaryPart ? 1 : 0)}; + return builder.create(loc, type, attr).getResult(); + } + + mlir::OpBuilder &builder; + mlir::Location loc; +}; + +} +#endif // FORTRAN_BURNSIDE_COMPLEX_H_ diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 3827a0a37477..1d7e6e6531ac 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -14,6 +14,7 @@ #include "convert-expr.h" #include "builder.h" +#include "complex.h" #include "fe-helper.h" #include "fir/Dialect.h" #include "fir/FIROps.h" @@ -69,6 +70,7 @@ class ExprLowering { M::OpBuilder &builder; SomeExpr const &expr; SymMap &symMap; + SymMap loadedSymbols{}; Co::IntrinsicTypeDefaultKinds const &defaults; IntrinsicLibrary const &intrinsics; @@ -107,7 +109,7 @@ class ExprLowering { /// Generate an integral constant of `value` template M::Value *genIntegerConstant(M::MLIRContext *context, std::int64_t value) { - M::Type type{getFIRType(context, defaults, IntegerCat, 8)}; + M::Type type{getFIRType(context, defaults, IntegerCat, KIND)}; auto attr{builder.getIntegerAttr(type, value)}; auto res{builder.create(getLoc(), type, attr)}; return res.getResult(); @@ -226,7 +228,16 @@ class ExprLowering { } M::Value *gendef(Se::Symbol const *sym) { return gen(sym); } M::Value *genval(Se::Symbol const *sym) { - return builder.create(getLoc(), gen(sym)); + // Do not load the same symbols several time in one expression. + // Fortran guarantees variable value must be the same wherever it + // appears in one expression. + if (mlir::Value * loaded{loadedSymbols.lookupSymbol(sym)}) { + return loaded; + } else { + mlir::Value *load{builder.create(getLoc(), gen(sym))}; + loadedSymbols.addSymbol(sym, load); + return load; + } } M::Value *genval(Ev::BOZLiteralConstant const &) { TODO(); } @@ -239,23 +250,23 @@ class ExprLowering { template M::Value *genval(Ev::TypeParamInquiry const &) { TODO(); } + template M::Value *genval(Ev::ComplexComponent const &part) { - auto *ctxt = builder.getContext(); - auto realTy{getFIRType(ctxt, defaults, RealCat, KIND)}; - auto index = genIntegerConstant<4>(ctxt, part.isImaginaryPart ? 1 : 0); - return builder.create( - getLoc(), realTy, genval(part.left()), index); + return ComplexHandler{builder, getLoc()}.createComplexPart( + genval(part.left()), part.isImaginaryPart); } + template M::Value *genval(Ev::Negate> const &) { TODO(); } + template M::Value *genval(Ev::Add> const &op) { if constexpr (TC == IntegerCat) { return createBinaryOp(op); } else if constexpr (TC == RealCat) { - return createBinaryOp(op); + return createBinaryOp(op); } else { static_assert(TC == ComplexCat, "Expected numeric type"); return createBinaryOp(op); @@ -266,7 +277,7 @@ class ExprLowering { if constexpr (TC == IntegerCat) { return createBinaryOp(op); } else if constexpr (TC == RealCat) { - return createBinaryOp(op); + return createBinaryOp(op); } else { static_assert(TC == ComplexCat, "Expected numeric type"); return createBinaryOp(op); @@ -277,7 +288,7 @@ class ExprLowering { if constexpr (TC == IntegerCat) { return createBinaryOp(op); } else if constexpr (TC == RealCat) { - return createBinaryOp(op); + return createBinaryOp(op); } else { static_assert(TC == ComplexCat, "Expected numeric type"); return createBinaryOp(op); @@ -288,7 +299,7 @@ class ExprLowering { if constexpr (TC == IntegerCat) { return createBinaryOp(op); } else if constexpr (TC == RealCat) { - return createBinaryOp(op); + return createBinaryOp(op); } else { static_assert(TC == ComplexCat, "Expected numeric type"); return createBinaryOp(op); @@ -310,18 +321,10 @@ class ExprLowering { M::Type ty{getFIRType(builder.getContext(), defaults, TC, KIND)}; return intrinsics.genval(getLoc(), builder, "pow", ty, operands); } + template M::Value *genval(Ev::ComplexConstructor const &op) { - auto *ctxt = builder.getContext(); - auto complexTy{fir::CplxType::get(ctxt, KIND)}; - auto loc{getLoc()}; - auto und = builder.create(loc, complexTy); - auto *lf = genval(op.left()); - auto *realIdx = genIntegerConstant<4>(ctxt, 0); - auto rp = - builder.create(loc, complexTy, und, lf, realIdx); - auto *rt = genval(op.right()); - auto *imagIdx = genIntegerConstant<4>(ctxt, 1); - return builder.create(loc, complexTy, rp, rt, imagIdx); + return ComplexHandler{builder, getLoc()}.createComplex( + KIND, genval(op.left()), genval(op.right())); } template M::Value *genval(Ev::Concat const &op) { // TODO this is a bogus call @@ -347,10 +350,20 @@ class ExprLowering { return createCompareOp(op, translateRelational(op.opr)); } else if constexpr (TC == RealCat) { return createFltCmpOp(op, translateFloatRelational(op.opr)); + } else if constexpr (TC == ComplexCat) { + bool eq{op.opr == Co::RelationalOperator::EQ}; + assert(eq || + op.opr == Co::RelationalOperator::NE && + "relation undefined for complex"); + return ComplexHandler{builder, getLoc()}.createComplexCompare( + genval(op.left()), genval(op.right()), eq); } else { + static_assert(TC == CharacterCat); TODO(); } } + + // TODO JP: the thing below should not be required. M::Value *genval(Ev::Relational const &op) { return std::visit([&](const auto &x) { return genval(x); }, op.u); } diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index ec351b710b55..3c0c947162ea 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -14,6 +14,7 @@ #include "intrinsics.h" #include "builder.h" +#include "complex.h" #include "fe-helper.h" #include "fir/FIROps.h" #include "fir/Type.h" @@ -111,7 +112,7 @@ class IntrinsicLibrary::Implementation { /// Define the different FIR generators that can be mapped to intrinsic to /// generate the related code. - using Generator = mlir::Value *(Implementation::*)(Context &) const; + using Generator = mlir::Value *(Implementation::*)(Context &)const; /// Search a runtime function that is associated to the generic intrinsic name /// and whose signature matches the intrinsic arguments and result types. /// If no such runtime function is found but a runtime function associated @@ -132,7 +133,7 @@ class IntrinsicLibrary::Implementation { mlir::Value *defaultGenerator(Context &c) const { return generateWrapperCall<&I::generateRuntimeCall>(c); } - mlir::Value *conjgGenerator(Context &) const; + mlir::Value *generateConjg(Context &) const; struct IntrinsicHanlder { const char *name; @@ -144,7 +145,7 @@ class IntrinsicLibrary::Implementation { /// defined here for a generic intrinsic, the defaultGenerator will /// be attempted. static constexpr IntrinsicHanlder handlers[]{ - {"conjg", &I::conjgGenerator}, + {"conjg", &I::generateConjg}, }; // helpers @@ -358,6 +359,8 @@ std::string IntrinsicLibrary::Implementation::getWrapperName(Context &c) { } else if (auto cplx{resultType.dyn_cast()}) { // TODO using kind here is weird, but I do not want to hard coded mapping return prefix.str() + c.name.str() + ".c" + std::to_string(cplx.getFKind()); + } else if (auto firf{resultType.dyn_cast()}) { + return prefix.str() + c.name.str() + ".f" + std::to_string(firf.getFKind()); } else { assert(false); return "fir." + c.name.str() + ".unknown"; @@ -447,31 +450,23 @@ mlir::Value *IntrinsicLibrary::Implementation::generateRuntimeCall( } // CONJG -mlir::Value *IntrinsicLibrary::Implementation::conjgGenerator( +mlir::Value *IntrinsicLibrary::Implementation::generateConjg( Context &genCtxt) const { assert(genCtxt.arguments.size() == 1); mlir::Type resType{genCtxt.getResultType()}; assert(resType == genCtxt.arguments[0]->getType()); + mlir::OpBuilder &builder{*genCtxt.builder}; + ComplexHandler cplxHandler{builder, genCtxt.loc}; - // FIXME make fir.complex.. instead of fir.complex<4> to avoid - // constantly having to translate to fe types ? Or get actual mapping - auto complexType{resType.cast()}; - mlir::Type realType{ - burnside::convertReal(genCtxt.getMLIRContext(), complexType.getFKind())}; - auto ity{mlir::IndexType::get(genCtxt.getMLIRContext())}; - auto oneAttr{genCtxt.builder->getIntegerAttr(ity, 1)}; - auto *imagIdx{ - genCtxt.builder->create(genCtxt.loc, ity, oneAttr) - .getResult()}; - auto parts{genCtxt.builder->create( - genCtxt.loc, realType, genCtxt.arguments[0], imagIdx)}; // TODO a negation unary would be better than a sub to zero ? - auto zeroAttr{genCtxt.builder->getFloatAttr(realType, llvm::APFloat{0.})}; - auto zero{genCtxt.builder->create( - genCtxt.loc, realType, zeroAttr)}; - auto negImag{genCtxt.builder->create(genCtxt.loc, zero, parts)}; - return genCtxt.builder->create( - genCtxt.loc, resType, genCtxt.arguments[0], negImag, imagIdx); + mlir::Value *cplx{genCtxt.arguments[0]}; + mlir::Type realType{cplxHandler.getComplexPartType(cplx)}; + mlir::Value *zero{builder.create( + genCtxt.loc, realType, builder.getZeroAttr(realType))}; + mlir::Value *imag{cplxHandler.createComplexImagPart(cplx)}; + mlir::Value *negImag{ + genCtxt.builder->create(genCtxt.loc, zero, imag)}; + return cplxHandler.setImagPart(cplx, negImag); } // RuntimeStaticDescription implementation From 36859a93c6273815f766293668042f7ce8321505 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Fri, 25 Oct 2019 04:43:00 -0700 Subject: [PATCH 025/123] Handle function result in ReturnStmt lowering Return statement were not returning any value for functions. The function generating return opertion was lacking knowledge about the result symbol to return a value. This commit solves this by: + Adding a pointer to the root of the parse tree being lowered in AnalysisData. + Restructuring the functions handling flat::ReturnOp to handle main, subroutine and functions differently and proagate the information needed to emit the related return ops + For functions, get the symbol result form FunctionSubprogram parse tree node, load its value and return the value. The knowledege of the parse tree root that can be flattened is now contained in a variant in AnalysisData so there is no need to pass the tree node in `CreateFlatFIR`. The template and its explicit instantiations can be removed and replaced by a `std::visit` inside the single `CreateFlatFIR` entry point. Conflicts: lib/burnside/bridge.cc --- lib/burnside/bridge.cc | 60 ++++++++++++++++++++++++++++++++------- lib/burnside/flattened.cc | 17 ++++------- lib/burnside/flattened.h | 17 +++++------ 3 files changed, 62 insertions(+), 32 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 22527ae4e055..fd552b6ce3bb 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -229,8 +229,12 @@ class FIRConverter { } noInsPt = true; } - void genFIR(const Fl::ReturnOp &op) { - std::visit([&](const auto *stmt) { genFIR(*stmt); }, op.u); + void genFIR(AnalysisData &ad, const Fl::ReturnOp &op) { + std::visit(Co::visitors{ + [&](const Pa::ReturnStmt *stmt) { genReturnStmt(ad, stmt); }, + [&](const auto *stmt) { genFIR(*stmt); }, + }, + op.u); noInsPt = true; } void genFIR(const Fl::ConditionalGotoOp &op) { @@ -386,7 +390,7 @@ class FIRConverter { void genEnterFIR(const Pa::OmpEndLoopDirective &) { TODO(); } void genEnterFIR(const Pa::SelectRankConstruct &) { TODO(); } void genEnterFIR(const Pa::SelectTypeConstruct &) { TODO(); } - + void genExitFIR(const Pa::DoConstruct &construct) { if (firLoopOp) { build().setInsertionPointAfter( @@ -458,9 +462,39 @@ class FIRConverter { build().create(toLocation(), callee, operands); build().create(toLocation()); } - void genFIR(const Pa::ReturnStmt &stmt) { - build().create(toLocation()); // FIXME: argument(s)? + void genReturnStmt(AnalysisData &, const Pa::FunctionSubprogram &func) { + auto &stmt{std::get>(func.t)}; + auto &name{std::get(stmt.statement.t)}; + assert(name.symbol); + const auto &details{name.symbol->get()}; + const Se::Symbol *result{&details.result()}; + M::Value *resultRef{symbolMap.lookupSymbol(result)}; + assert(resultRef); // FIXME might die if result + // was never referenced before and temp not created. + M::Value *resultVal{build().create(toLocation(), resultRef)}; + build().create(toLocation(), resultVal); + } + void genReturnStmt(const Pa::MainProgram &) { + build().create(toLocation()); + } + void genReturnStmt( + const Pa::SubroutineSubprogram &, const Pa::ReturnStmt * = nullptr) { + // TODO use Pa::ReturnStmt for alternate return + build().create(toLocation()); + } + void genReturnStmt(AnalysisData &ad, const Pa::ReturnStmt *stmt = nullptr) { + std::visit(Co::visitors{ + [&](const Pa::SubroutineSubprogram *sub) { + genReturnStmt(*sub, stmt); + }, + [&](const Pa::FunctionSubprogram *func) { + genReturnStmt(ad, *func); + }, + [&](const Pa::MainProgram *main) { genReturnStmt(*main); }, + }, + ad.parseTreeRoot); } + void genFIR(const Pa::StopStmt &stmt) { auto callee{genRuntimeFunction( isStopStmt(std::get(stmt.t)) ? FIRT_STOP @@ -858,11 +892,13 @@ void FIRConverter::genFIR(AnalysisData &ad, const Fl::ActionOp &op) { [](const Co::Indirection &) { TODO(); }, [](const Co::Indirection &) { TODO(); }, [](const Co::Indirection &) { TODO(); }, - [](const Co::Indirection &) { TODO(); }, [](const Co::Indirection &) { TODO(); }, [&](const Co::Indirection &assign) { genFIR(ad, assign.value()); }, + [](const Co::Indirection &) { + assert(false && "should be a ReturnOp"); + }, [&](const auto &stmt) { genFIR(stmt); }, }, op.v->statement.u); @@ -895,6 +931,11 @@ void FIRConverter::genFIR(AnalysisData &ad, std::list &operations) { genFIR(oper); lastWasLabel = true; }, + [&](const Fl::ReturnOp &oper) { + noInsPt = false; + genFIR(ad, oper); + lastWasLabel = true; + }, [&](const auto &oper) { noInsPt = false; genFIR(oper); @@ -904,8 +945,7 @@ void FIRConverter::genFIR(AnalysisData &ad, std::list &operations) { op.u); } if (build().getInsertionBlock()) { - // FIXME: assuming type of '() -> ()' - build().create(toLocation()); + genReturnStmt(ad); } } @@ -955,9 +995,9 @@ void FIRConverter::translateRoutine( assert(false && "symbol misidentified by front-end"); } } - AnalysisData ad; + AnalysisData ad{routine}; std::list operations; - CreateFlatIR(routine, operations, ad); + CreateFlatIR(operations, ad); genFIR(ad, operations); finalizeQueued(); } diff --git a/lib/burnside/flattened.cc b/lib/burnside/flattened.cc index 3d7989dcda9e..0a5f6c91e9b2 100644 --- a/lib/burnside/flattened.cc +++ b/lib/burnside/flattened.cc @@ -75,8 +75,8 @@ std::vector GetAssign( } static std::tuple FindStack( - const std::vector> - &stack, + const std::vector< + std::tuple> &stack, const parser::Name *key) { for (auto iter{stack.rbegin()}, iend{stack.rend()}; iter != iend; ++iter) { if (std::get<0>(*iter) == key) { @@ -705,17 +705,10 @@ struct ControlFlowAnalyzer { } // namespace flat -template -void CreateFlatIR(const A &ptree, std::list &ops, AnalysisData &ad) { +void CreateFlatIR(std::list &ops, AnalysisData &ad) { flat::ControlFlowAnalyzer linearize{ops, ad}; - Walk(ptree, linearize); + std::visit( + [&](const auto *ptree) { Walk(*ptree, linearize); }, ad.parseTreeRoot); } -#define INSTANTIATE_EXPLICITLY(T) \ - template void CreateFlatIR( \ - const parser::T &, std::list &, AnalysisData &) -INSTANTIATE_EXPLICITLY(MainProgram); -INSTANTIATE_EXPLICITLY(FunctionSubprogram); -INSTANTIATE_EXPLICITLY(SubroutineSubprogram); - } // namespace burnside diff --git a/lib/burnside/flattened.h b/lib/burnside/flattened.h index f5c795c04eaa..36c32c3c98d0 100644 --- a/lib/burnside/flattened.h +++ b/lib/burnside/flattened.h @@ -214,24 +214,21 @@ std::vector GetAssign( // Collection of data maintained internally by the flattening algorithm struct AnalysisData { + template + AnalysisData(const Node &node) : parseTreeRoot{&node} {} + std::map labelMap; std::vector< std::tuple> constructContextStack; flat::LabelBuilder labelBuilder; std::map> assignMap; + const std::variant + parseTreeRoot; }; -// entry-point into building the flat IR -template -void CreateFlatIR(const A &ptree, std::list &ops, AnalysisData &ad); - -#define EXPLICIT_INSTANTIATION(T) \ - extern template void CreateFlatIR( \ - const parser::T &, std::list &, AnalysisData &) -EXPLICIT_INSTANTIATION(MainProgram); -EXPLICIT_INSTANTIATION(FunctionSubprogram); -EXPLICIT_INSTANTIATION(SubroutineSubprogram); +void CreateFlatIR(std::list &, AnalysisData &); } // namespace burnside From bb1cfa5fd8a2417e38da62ec5ff6fc7b6714604c Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Fri, 25 Oct 2019 04:55:41 -0700 Subject: [PATCH 026/123] Generate FIR for funciton calls in expressions. + This is a primitive implementation that only works with implicit interface and when the arguments are variables (i.e. symbols). --- lib/burnside/convert-expr.cc | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 1d7e6e6531ac..d5528844ed1d 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -153,6 +153,8 @@ class ExprLowering { M::FuncOp getFunction(L::StringRef name, M::FunctionType funTy) { auto module{getModule(&builder)}; if (M::FuncOp func{getNamedFunction(module, name)}) { + assert(func.getType() == funTy && + "function already declared with a different type"); return func; } return createFunction(module, name, funTy); @@ -628,7 +630,31 @@ class ExprLowering { L::StringRef name{intrinsic->name}; return intrinsics.genval(getLoc(), builder, name, ty, operands); } else { - TODO(); // User defined function. + // implicit interface implementation only + // TODO: explicit interface + L::SmallVector argTypes; + L::SmallVector operands; + for (const auto &arg : funRef.arguments()) { + assert( + arg.has_value() && "optional argument requires explicit interface"); + const auto *expr{arg->UnwrapExpr()}; + assert(expr && "assumed type argument requires explicit interface"); + if (const Se::Symbol * sym{Ev::UnwrapWholeSymbolDataRef(*expr)}) { + M::Value *argRef{symMap.lookupSymbol(sym)}; + assert(argRef && "could not get symbol reference"); + argTypes.push_back(argRef->getType()); + operands.push_back(argRef); + } else { + // TODO create temps for expressions + TODO(); + } + } + M::Type resultType{getFIRType(builder.getContext(), defaults, TC, KIND)}; + M::FunctionType funTy{ + M::FunctionType::get(argTypes, resultType, builder.getContext())}; + M::FuncOp func{getFunction(funRef.proc().GetName(), funTy)}; + M::CallOp call{builder.create(getLoc(), func, operands)}; + return call.getResult(0); } } From a739ddba4ea91c5a7a5110c856f616c0d473ef04 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Mon, 28 Oct 2019 10:32:05 -0700 Subject: [PATCH 027/123] Reorganize Maths Runtime description to prepare for other runtimes + Strip out the part that are not specific to maths runtime into runtime.cc and runtime.h so that they can be used by future other runtime description. (RuntimeStaticDescription and TypeCode) + Provide a new StaticMultimapView that can provide constexpr multimap search functions over a static constexpr tables. This is to provide a way to handle runtime description without needing to dynamicaaly instanciate a map and hold it somewhere. For now, it is not used on the math runtime so that we can compare both strategies. --- lib/burnside/intrinsics.cc | 81 +++++------------------ lib/burnside/runtime.cc | 53 ++++++++++++++- lib/burnside/runtime.h | 130 ++++++++++++++++++++++++++++++++++++- 3 files changed, 196 insertions(+), 68 deletions(-) diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index 3c0c947162ea..4e514e6ee5d1 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -18,6 +18,7 @@ #include "fe-helper.h" #include "fir/FIROps.h" #include "fir/Type.h" +#include "runtime.h" #include "llvm/ADT/Optional.h" #include #include @@ -159,48 +160,22 @@ class IntrinsicLibrary::Implementation { /// Define a simple static runtime description that will be transformed into /// RuntimeFunction when building the IntrinsicLibrary. -/// It is constexpr constructible so that static tables of such descriptions can -/// be safely stored as global variables without requiring global constructors. -class RuntimeStaticDescription { +class MathsRuntimeStaticDescription : public RuntimeStaticDescription { public: - /// Define possible runtime function argument/return type used in signature - /// descriptions. They follow mlir standard types naming. MLIR types cannot - /// directly be used because they can only be dynamically built. - enum class TypeCode { i32, i64, f32, f64, c32, c64 }; - /// C++ does not provide variable size constexpr container yet. TypeVector - /// implements one for Type elements. It works because Type is an enumeration. - struct TypeCodeVector { - template struct Storage { - static constexpr TypeCode values[]{v...}; - }; - template static constexpr TypeCodeVector create() { - const TypeCode *start{&Storage::values[0]}; - return TypeCodeVector{start, start + sizeof...(v)}; - } - const TypeCode *start{nullptr}; - const TypeCode *end{nullptr}; - }; - constexpr RuntimeStaticDescription( - const char *n, const char *s, TypeCode r, TypeCodeVector a) - : name{n}, symbol{s}, resultTypeCode{r}, argumentTypeCodes{a} {} - llvm::StringRef getSymbol() const { return symbol; } + constexpr MathsRuntimeStaticDescription( + const char *n, const char *s, MaybeTypeCode r, TypeCodeVector a) + : RuntimeStaticDescription{s, r, a}, name{n} {} llvm::StringRef getName() const { return name; } - /// Conversion between types of the static representation and MLIR types. - mlir::FunctionType getMLIRFunctionType(mlir::MLIRContext &) const; private: - static mlir::Type getMLIRType(TypeCode, mlir::MLIRContext &); - + // Generic math function name const char *name{nullptr}; - const char *symbol{nullptr}; - TypeCode resultTypeCode; - TypeCodeVector argumentTypeCodes; }; /// Description of the runtime functions available on the target. using RType = typename RuntimeStaticDescription::TypeCode; using Args = typename RuntimeStaticDescription::TypeCodeVector; -static constexpr RuntimeStaticDescription llvmRuntime[] = { +static constexpr MathsRuntimeStaticDescription llvmRuntime[] = { {"abs", "llvm.fabs.f32", RType::f32, Args::create()}, {"abs", "llvm.fabs.f64", RType::f64, Args::create()}, {"acos", "acosf", RType::f32, Args::create()}, @@ -215,7 +190,7 @@ static constexpr RuntimeStaticDescription llvmRuntime[] = { {"sin", "llvm.sin.f64", RType::f64, Args::create()}, }; -static constexpr RuntimeStaticDescription pgmathPreciseRuntime[] = { +static constexpr MathsRuntimeStaticDescription pgmathPreciseRuntime[] = { {"acos", "__pc_acos_1", RType::c32, Args::create()}, {"acos", "__pz_acos_1", RType::c64, Args::create()}, {"pow", "__pc_pow_1", RType::c32, Args::create()}, @@ -266,13 +241,15 @@ mlir::Value *IntrinsicLibrary::genval(mlir::Location loc, // Create the runtime description for the targeted library version. // So far ignore the version an only load the dummy llvm lib and pgmath precise MathRuntimeLibrary::MathRuntimeLibrary( - IntrinsicLibrary::Version, mlir::MLIRContext &context) { - for (const RuntimeStaticDescription &func : llvmRuntime) { - RuntimeFunction impl{func.getSymbol(), func.getMLIRFunctionType(context)}; + IntrinsicLibrary::Version, mlir::MLIRContext &mlirContext) { + for (const MathsRuntimeStaticDescription &func : llvmRuntime) { + RuntimeFunction impl{ + func.getSymbol(), func.getMLIRFunctionType(&mlirContext)}; library.insert({Key{func.getName()}, impl}); } - for (const RuntimeStaticDescription &func : pgmathPreciseRuntime) { - RuntimeFunction impl{func.getSymbol(), func.getMLIRFunctionType(context)}; + for (const MathsRuntimeStaticDescription &func : pgmathPreciseRuntime) { + RuntimeFunction impl{ + func.getSymbol(), func.getMLIRFunctionType(&mlirContext)}; library.insert({Key{func.getName()}, impl}); } } @@ -469,32 +446,4 @@ mlir::Value *IntrinsicLibrary::Implementation::generateConjg( return cplxHandler.setImagPart(cplx, negImag); } -// RuntimeStaticDescription implementation - -mlir::Type RuntimeStaticDescription::getMLIRType( - TypeCode t, mlir::MLIRContext &context) { - switch (t) { - case TypeCode::i32: return mlir::IntegerType::get(32, &context); - case TypeCode::i64: return mlir::IntegerType::get(64, &context); - case TypeCode::f32: return mlir::FloatType::getF32(&context); - case TypeCode::f64: return mlir::FloatType::getF64(&context); - // TODO need to access mapping between fe/target - case TypeCode::c32: return fir::CplxType::get(&context, 4); - case TypeCode::c64: return fir::CplxType::get(&context, 8); - } - assert(false && "bug"); - return {}; -} - -mlir::FunctionType RuntimeStaticDescription::getMLIRFunctionType( - mlir::MLIRContext &context) const { - llvm::SmallVector argMLIRTypes; - for (const TypeCode *t{argumentTypeCodes.start}; - t != nullptr && t != argumentTypeCodes.end; ++t) { - argMLIRTypes.push_back(getMLIRType(*t, context)); - } - mlir::Type resMLIRType{getMLIRType(resultTypeCode, context)}; - return mlir::FunctionType::get(argMLIRTypes, resMLIRType, &context); -} - } diff --git a/lib/burnside/runtime.cc b/lib/burnside/runtime.cc index dc14ce642918..453dbb9faf64 100644 --- a/lib/burnside/runtime.cc +++ b/lib/burnside/runtime.cc @@ -13,6 +13,7 @@ // limitations under the License. #include "runtime.h" +#include "builder.h" #include "fir/Type.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" @@ -21,8 +22,58 @@ #include "mlir/IR/Types.h" #include -namespace Br = Fortran::burnside; +namespace Fortran::burnside { +mlir::Type RuntimeStaticDescription::getMLIRType( + TypeCode t, mlir::MLIRContext *context) { + switch (t) { + case TypeCode::i32: return mlir::IntegerType::get(32, context); + case TypeCode::i64: return mlir::IntegerType::get(64, context); + case TypeCode::f32: return mlir::FloatType::getF32(context); + case TypeCode::f64: return mlir::FloatType::getF64(context); + // TODO need to access mapping between fe/target + case TypeCode::c32: return fir::CplxType::get(context, 4); + case TypeCode::c64: return fir::CplxType::get(context, 8); + // ! IOCookie is experimental only so far + case TypeCode::IOCookie: + return fir::ReferenceType::get(mlir::IntegerType::get(64, context)); + } + assert(false && "bug"); + return {}; +} + +mlir::FunctionType RuntimeStaticDescription::getMLIRFunctionType( + mlir::MLIRContext *context) const { + llvm::SmallVector argMLIRTypes; + for (const TypeCode *t{argumentTypeCodes.start}; + t != nullptr && t != argumentTypeCodes.end; ++t) { + argMLIRTypes.push_back(getMLIRType(*t, context)); + } + if (resultTypeCode.has_value()) { + mlir::Type resMLIRType{getMLIRType(*resultTypeCode, context)}; + return mlir::FunctionType::get(argMLIRTypes, resMLIRType, context); + } else { + return mlir::FunctionType::get(argMLIRTypes, {}, context); + } +} +mlir::FuncOp RuntimeStaticDescription::getFuncOp( + mlir::OpBuilder &builder) const { + mlir::ModuleOp module{getModule(&builder)}; + mlir::FunctionType funTy{getMLIRFunctionType(module.getContext())}; + auto function{getNamedFunction(module, symbol)}; + if (!function) { + function = createFunction(module, symbol, funTy); + function.setAttr("fir.runtime", builder.getUnitAttr()); + } else { + assert(funTy == function.getType() && "conflicting runtime declaration"); + } + return function; +} +} + +// TODO remove dependencies to stub rt below + +namespace Br = Fortran::burnside; using namespace Fortran; using namespace Fortran::burnside; diff --git a/lib/burnside/runtime.h b/lib/burnside/runtime.h index 48096775bc25..86f3c208f35b 100644 --- a/lib/burnside/runtime.h +++ b/lib/burnside/runtime.h @@ -15,20 +15,148 @@ #ifndef FORTRAN_BURNSIDE_RUNTIME_H_ #define FORTRAN_BURNSIDE_RUNTIME_H_ -#include +#include namespace llvm { class StringRef; } namespace mlir { +class Type; class FunctionType; class MLIRContext; +class OpBuilder; +class FuncOp; } namespace Fortran::burnside { /// [Coding style](https://llvm.org/docs/CodingStandards.html) +/// Define a simple static runtime description that different runtime can +/// derived from (e.g io, maths ...). +/// This base class only define enough to generate the functuion declarations, +// it is up to the actual runtime descriptions to define a way to organize these +// descriptions in a meaningful way. +/// It is constexpr constructible so that static tables of such descriptions can +/// be safely stored as global variables without requiring global constructors. +class RuntimeStaticDescription { +public: + /// Define possible runtime function argument/return type used in signature + /// descriptions. They follow mlir standard types naming. MLIR types cannot + /// directly be used because they can only be dynamically built. + enum TypeCode { i32, i64, f32, f64, c32, c64, IOCookie }; + using MaybeTypeCode = std::optional; // for results + static constexpr MaybeTypeCode voidTy{MaybeTypeCode{std::nullopt}}; + + /// C++ does not provide variable size constexpr container yet. TypeVector + /// implements one for Type elements. It works because Type is an enumeration. + struct TypeCodeVector { + template struct Storage { + static constexpr TypeCode values[]{v...}; + }; + template static constexpr TypeCodeVector create() { + const TypeCode *start{&Storage::values[0]}; + return TypeCodeVector{start, start + sizeof...(v)}; + } + template<> constexpr TypeCodeVector create<>() { return TypeCodeVector{}; } + const TypeCode *start{nullptr}; + const TypeCode *end{nullptr}; + }; + constexpr RuntimeStaticDescription( + const char *s, MaybeTypeCode r, TypeCodeVector a) + : symbol{s}, resultTypeCode{r}, argumentTypeCodes{a} {} + const char *getSymbol() const { return symbol; } + /// Conversion between types of the static representation and MLIR types. + mlir::FunctionType getMLIRFunctionType(mlir::MLIRContext *) const; + mlir::FuncOp getFuncOp(mlir::OpBuilder &) const; + static mlir::Type getMLIRType(TypeCode, mlir::MLIRContext *); + +private: + const char *symbol{nullptr}; + MaybeTypeCode resultTypeCode; + TypeCodeVector argumentTypeCodes; +}; + +/// StaticMultimapView is a constexpr friendly multimap +/// implementation over sorted constexpr arrays. +/// As the View name suggests, it does not duplicate the +/// sorted array but only brings range and search concepts +/// over it. It provides compile time search and can also +/// provide dynamic search (currently linear, can be improved to +/// log(n) due to the sorted array property). + +// TODO: Find a better place for this if this is retained. +// This is currently here because this was designed to provide +// maps over runtime description without the burden of having to +// instantiate these maps dynamically and to keep they somewhere. +template class StaticMultimapView { +public: + using Key = typename Value::Key; + struct Range { + using const_iterator = const Value *; + constexpr const_iterator begin() const { return startPtr; } + constexpr const_iterator end() const { return endPtr; } + constexpr bool empty() const { + return startPtr == nullptr || endPtr == nullptr || endPtr <= startPtr; + } + constexpr std::size_t size() const { + return empty() ? 0 : static_cast(endPtr - startPtr); + } + const Value *startPtr{nullptr}; + const Value *endPtr{nullptr}; + }; + using const_iterator = typename Range::const_iterator; + + template + constexpr StaticMultimapView(const Value (&array)[N]) + : range{&array[0], &array[0] + N} {} + template constexpr bool verify() { + // TODO: sorted + // non empty increasing pointer direction + return !range.empty(); + }; + constexpr const_iterator begin() const { return range.begin(); } + constexpr const_iterator end() const { return range.end(); } + + // Assume array is sorted. + // TODO make it a log(n) search based on sorted property + // std::equal_range will be constexpr in C++20 only. + constexpr Range getRange(const Key &key) const { + bool matched{false}; + const Value *start{nullptr}, *end{nullptr}; + for (const auto &desc : range) { + if (desc.key == key) { + if (!matched) { + start = &desc; + matched = true; + } + } else if (matched) { + end = &desc; + matched = false; + } + } + if (matched) { + end = range.end(); + } + return Range{start, end}; + } + + constexpr std::pair equal_range( + const Key &key) const { + Range range{getRange(key)}; + return {range.begin(), range.end()}; + } + + constexpr typename Range::const_iterator find(Key key) const { + const Range subRange{getRange(key)}; + return subRange.size() == 1 ? subRange.begin() : end(); + } + +private: + Range range{nullptr, nullptr}; +}; +// TODO get rid of fake runtime below + #define DEFINE_RUNTIME_ENTRY(A, B, C, D) FIRT_##A, enum RuntimeEntryCode { #include "runtime.def" From 9baaa3b8b26f6bdbc0abe02e1a67c720622be6f3 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Fri, 18 Oct 2019 00:06:56 -0700 Subject: [PATCH 028/123] Add IO runtime description framework It uses what was made generic from the maths intrinsic runtime. The main differences is that it use an enum based key to sort the functions instead of math function names and also that it use the newly created constexpr multimap to avoid having to create a dynamic map to handle it. The runtime described so far is experimental. Conflicts: lib/burnside/bridge.cc --- lib/burnside/CMakeLists.txt | 1 + lib/burnside/bridge.cc | 15 ++++- lib/burnside/io.cc | 130 ++++++++++++++++++++++++++++++++++++ lib/burnside/io.h | 38 +++++++++++ 4 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 lib/burnside/io.cc create mode 100644 lib/burnside/io.h diff --git a/lib/burnside/CMakeLists.txt b/lib/burnside/CMakeLists.txt index 5a29de117ecb..c4b1c3e9b6ca 100644 --- a/lib/burnside/CMakeLists.txt +++ b/lib/burnside/CMakeLists.txt @@ -22,6 +22,7 @@ add_library(FortranBurnside flattened.cc intrinsics.cc runtime.cc + io.cc ) target_link_libraries(FortranBurnside diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index fd552b6ce3bb..0897ea911cf1 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -21,6 +21,7 @@ #include "fir/Type.h" #include "flattened.h" #include "intrinsics.h" +#include "io.h" #include "runtime.h" #include "../parser/parse-tree-visitor.h" #include "../semantics/tools.h" @@ -600,7 +601,19 @@ class FIRConverter { void genFIR(const Pa::NullifyStmt &stmt) { TODO(); } void genFIR(const Pa::OpenStmt &stmt) { TODO(); } void genFIR(const Pa::PointerAssignmentStmt &stmt) { TODO(); } - void genFIR(const Pa::PrintStmt &stmt) { TODO(); } + void genFIR(const Pa::PrintStmt &stmt) { + llvm::SmallVector args; + for (const Pa::OutputItem &item : + std::get>(stmt.t)) { + if (const Pa::Expr * parserExpr{std::get_if(&item.u)}) { + mlir::Location loc{toLocation(parserExpr->source)}; + args.push_back(createFIRExpr(loc, Se::GetExpr(*parserExpr))); + } else { + assert(false); // implied do TODO + } + } + genPrintStatement(build(), toLocation(lastKnownPos), args); + } void genFIR(const Pa::ReadStmt &stmt) { TODO(); } void genFIR(const Pa::RewindStmt &stmt) { TODO(); } void genFIR(const Pa::SyncAllStmt &stmt) { TODO(); } diff --git a/lib/burnside/io.cc b/lib/burnside/io.cc new file mode 100644 index 000000000000..038e022fa625 --- /dev/null +++ b/lib/burnside/io.cc @@ -0,0 +1,130 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "io.h" +#include "builder.h" +#include "runtime.h" +#include "mlir/Dialect/StandardOps/Ops.h" +#include "mlir/IR/Builders.h" + +namespace Fortran::burnside { + +/// Define actions to sort runtime functions. One actions +/// may be associated to one or more runtime function. +/// Actions are the keys in the StaticMultimapView used to +/// hold the io runtime description in a static constexpr way. +enum class IOAction { BeginExternalList, Output, EndIO }; + +class IORuntimeDescription : public RuntimeStaticDescription { +public: + using Key = IOAction; + constexpr IORuntimeDescription( + IOAction act, const char *s, MaybeTypeCode r, TypeCodeVector a) + : RuntimeStaticDescription{s, r, a}, key{act} {} + static mlir::Type getIOCookieType(mlir::MLIRContext *context) { + return getMLIRType(TypeCode::IOCookie, context); + } + IOAction key; +}; + +using IORuntimeMap = StaticMultimapView; + +using RT = RuntimeStaticDescription; +using RType = typename RT::TypeCode; +using Args = typename RT::TypeCodeVector; +using IOA = IOAction; + +/// This is were the IO runtime are to be described. +/// The array need to be sorted on the Actions. +/// Experimental runtime for now. +static constexpr IORuntimeDescription ioRuntimeTable[]{ + {IOA::BeginExternalList, "__F18IOa_BeginExternalListOutput", + RType::IOCookie, Args::create()}, + {IOA::Output, "__F18IOa_OutputInteger64", RT::voidTy, + Args::create()}, + {IOA::Output, "__F18IOa_OutputReal64", RT::voidTy, + Args::create()}, + {IOA::EndIO, "__F18IOa_EndIOStatement", RT::voidTy, + Args::create()}, +}; + +static constexpr IORuntimeMap ioRuntimeMap{ioRuntimeTable}; + +/// This helper can be used to access io runtime functions that +/// are mapped to an IOAction that must be mapped to one and +/// exactly one runtime function. This constraint is enforced +/// at compile time. This search is resolved at compile time. +template +static mlir::FuncOp getIORuntimeFunction(mlir::OpBuilder &builder) { + static constexpr auto runtimeDescription{ioRuntimeMap.find(key)}; + static_assert(runtimeDescription != ioRuntimeMap.end()); + return runtimeDescription->getFuncOp(builder); +} + +/// This helper can be used to access io runtime functions that +/// are mapped to Output IOAction that must be mapped to at least one +/// runtime function but can be mapped to more functions. +/// This helper returns the function that has the same +/// mlir::FunctionType as the one seeked. It may therefore dynamically fail +/// if no function mapped to the Action has the seeked mlir::FunctionType. +static mlir::FuncOp getOutputRuntimeFunction( + mlir::OpBuilder &builder, mlir::Type type) { + static constexpr auto descriptionRange{ioRuntimeMap.getRange(IOA::Output)}; + static_assert(!descriptionRange.empty()); + + mlir::MLIRContext *context{getModule(&builder).getContext()}; + llvm::SmallVector argTypes{ + IORuntimeDescription::getIOCookieType(context), type}; + + mlir::FunctionType seekedType{mlir::FunctionType::get(argTypes, {}, context)}; + for (const auto &description : descriptionRange) { + if (description.getMLIRFunctionType(context) == seekedType) { + return description.getFuncOp(builder); + } + } + assert(false && "IO output runtime function not defined for this type"); + return {}; +} + +/// Lower print statement assuming a dummy runtime interface for now. +void genPrintStatement(mlir::OpBuilder &builder, mlir::Location loc, + llvm::ArrayRef args) { + mlir::ModuleOp module{getModule(&builder)}; + mlir::MLIRContext *mlirContext{module.getContext()}; + + mlir::FuncOp beginFunc{ + getIORuntimeFunction(builder)}; + + // Initiate io + mlir::Type externalUnitType{mlir::IntegerType::get(32, mlirContext)}; + mlir::Value *defaultUnit{builder.create( + loc, builder.getIntegerAttr(externalUnitType, 1))}; + llvm::SmallVector beginArgs{defaultUnit}; + mlir::Value *cookie{ + builder.create(loc, beginFunc, beginArgs).getResult(0)}; + + // Call data transfer runtime function + for (mlir::Value *arg : args) { + llvm::SmallVector operands{cookie, arg}; + mlir::FuncOp outputFunc{getOutputRuntimeFunction(builder, arg->getType())}; + builder.create(loc, outputFunc, operands); + } + + // Terminate IO + mlir::FuncOp endIOFunc{getIORuntimeFunction(builder)}; + llvm::SmallVector endArgs{cookie}; + builder.create(loc, endIOFunc, endArgs); +} + +} diff --git a/lib/burnside/io.h b/lib/burnside/io.h new file mode 100644 index 000000000000..bae368609d9b --- /dev/null +++ b/lib/burnside/io.h @@ -0,0 +1,38 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_IO_H_ +#define FORTRAN_BURNSIDE_IO_H_ + +namespace mlir { +class OpBuilder; +class Location; +class Value; +} +namespace llvm { +template class ArrayRef; +} + +/// Experimental IO lowering to FIR + runtime. The Runtime design is under +/// design. +/// FIXME This interface is also not final. Should it be based on parser::.. +/// nodes and lower expressions as needed or should it get every expression +/// already lowered as mlir::Value* ? (currently second options, not sure it +/// will provide enough information for complex IO statements). +namespace Fortran::burnside { +void genPrintStatement( + mlir::OpBuilder &, mlir::Location loc, llvm::ArrayRef); +} + +#endif // FORTRAN_BURNSIDE_IO_H_ From ae3c9459f57f7dc182a9566f3071b3b8127ac193 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Mon, 28 Oct 2019 09:42:58 -0700 Subject: [PATCH 029/123] work on extract_value and insert_value add primitives for REAL and COMPLEX comparison and unary negation Fix TypeCodeVector GCC compile issues. GCC has trouble with template specialization in classes. lowering of select and select-rank ops Implement MAX and MIN lowering Lower to MERGE + comparisons to avoid introducing branches. add primitives for REAL and COMPLEX comparison and unary negation lowering of select and select-rank ops Use SymbolRef in Burnside bridge add pure calls to CSE fix caching bug in mem2reg select case on ints remove fir processing from bbc; finish adding fir.len type Add a rough draft of a name mangling scheme. This is unofficial and a mere starting point. Rework fir.field_index and fie.len_param_index operations; fix bbc link issue work on coordinate lowering; add internal name generator add fir.constant --- README.md | 29 +- .../BijectiveInternalNameMangling.md | 115 ++ documentation/FIRLangRef.md | 99 +- include/fir/Attribute.h | 29 +- include/fir/{Dialect.h => FIRDialect.h} | 19 +- include/fir/FIROps.h | 37 + include/fir/FIROps.td | 424 ++++-- include/fir/FIROpsSupport.h | 48 +- include/fir/{Type.h => FIRType.h} | 50 +- include/fir/InternalNames.h | 89 ++ include/fir/Tilikum/Tilikum.h | 5 +- include/fir/Transforms/Passes.h | 5 + lib/burnside/bridge.cc | 39 +- lib/burnside/builder.cc | 8 +- lib/burnside/builder.h | 4 +- lib/burnside/{complex.h => complex-handler.h} | 67 +- lib/burnside/convert-expr.cc | 104 +- lib/burnside/fe-helper.cc | 20 +- lib/burnside/fe-helper.h | 4 +- lib/burnside/intrinsics.cc | 142 +- lib/burnside/runtime.cc | 7 +- lib/burnside/runtime.h | 67 +- lib/fir/Attribute.cpp | 170 +-- lib/fir/CMakeLists.txt | 5 +- lib/fir/{Dialect.cpp => FIRDialect.cpp} | 46 +- lib/fir/FIROps.cpp | 223 +++- lib/fir/{Type.cpp => FIRType.cpp} | 1175 ++++++----------- lib/fir/InternalNames.cpp | 141 ++ lib/fir/StdConverter.cpp | 17 +- lib/fir/Tilikum.cpp | 1147 +++++++++++----- lib/fir/Transforms/CSE.cpp | 13 +- lib/fir/Transforms/MemToReg.cpp | 140 +- test/fir/aggregate.fir | 11 + test/fir/alloc.fir | 21 + test/fir/bugs/bug0001.fir | 41 + test/fir/bugs/bug0002.fir | 12 + test/fir/compare.fir | 29 + test/fir/complex.fir | 15 + test/fir/constant.fir | 19 + test/fir/dynlayout.fir | 38 + test/fir/embox.fir | 4 +- test/fir/fir-ops.fir | 44 +- test/fir/fir-types.fir | 16 +- test/fir/loads.fir | 35 + test/fir/select.fir | 45 + tools/CMakeLists.txt | 3 +- tools/{fml => bbc}/CMakeLists.txt | 12 +- tools/{fml/fml.cc => bbc/bbc.cc} | 82 +- tools/f18/CMakeLists.txt | 8 +- tools/tco/CMakeLists.txt | 46 + tools/tco/tco.cpp | 87 ++ 51 files changed, 3262 insertions(+), 1794 deletions(-) create mode 100644 documentation/BijectiveInternalNameMangling.md rename include/fir/{Dialect.h => FIRDialect.h} (70%) rename include/fir/{Type.h => FIRType.h} (90%) create mode 100644 include/fir/InternalNames.h rename lib/burnside/{complex.h => complex-handler.h} (65%) rename lib/fir/{Dialect.cpp => FIRDialect.cpp} (54%) rename lib/fir/{Type.cpp => FIRType.cpp} (53%) create mode 100644 lib/fir/InternalNames.cpp create mode 100644 test/fir/aggregate.fir create mode 100644 test/fir/alloc.fir create mode 100644 test/fir/bugs/bug0001.fir create mode 100644 test/fir/bugs/bug0002.fir create mode 100644 test/fir/compare.fir create mode 100644 test/fir/constant.fir create mode 100644 test/fir/dynlayout.fir create mode 100644 test/fir/loads.fir create mode 100644 test/fir/select.fir rename tools/{fml => bbc}/CMakeLists.txt (85%) rename tools/{fml/fml.cc => bbc/bbc.cc} (89%) create mode 100644 tools/tco/CMakeLists.txt create mode 100644 tools/tco/tco.cpp diff --git a/README.md b/README.md index 66cf6dc6a834..b5e1b09224c2 100644 --- a/README.md +++ b/README.md @@ -10,34 +10,47 @@ Working branch for FIR development. +## Monorepo + +This is quite similar to the old way, but with a few subtle differences. 1. Get the stuff. ``` - git clone http://llvm.org/git/llvm.git - git clone git@github.com:schweitzpgi/mlir.git + git clone git@github.com:flang-compiler/f18-llvm-project.git + git clone git@github.com:flang-compiler/f18-mlir.git git clone git@github.com:schweitzpgi/f18.git ``` 2. Get "on" the right branches. ``` - (cd llvm; git checkout master) - (cd mlir; git checkout f18) - (cd f18; git checkout f18) + (cd f18-llvm-project ; git checkout f18) + (cd f18-mlir ; git checkout f18) + (cd f18 ; git checkout f18) ``` 3. Setup the LLVM space for in-tree builds. ``` - cd llvm/projects ; ln -s ../../mlir . - cd llvm/tools ; ln -s ../../f18 flang + (cd f18-llvm-project/llvm/projects ; ln -s ../../../f18-mlir mlir) + (cd f18-llvm-project ; ln -s ../f18 flang) ``` 4. Create a build space for cmake and make/ninja ``` - mkdir build; cd build; cmake /path/to/llvm -DCMAKE_BUILD_TYPE=Debug -DLLVM_TARGETS_TO_BUILD=X86 ... + mkdir build + cd build + cmake ../f18-llvm-project/llvm -DCMAKE_BUILD_TYPE=Debug -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_ENABLE_PROJECTS=flang -DCMAKE_CXX_STANDARD=17 ``` +5. Build everything + +One can, for example, do this with make as follows. + +``` + make +``` +Or, of course, use their favorite build tool (such as ninja). diff --git a/documentation/BijectiveInternalNameMangling.md b/documentation/BijectiveInternalNameMangling.md new file mode 100644 index 000000000000..51801d328232 --- /dev/null +++ b/documentation/BijectiveInternalNameMangling.md @@ -0,0 +1,115 @@ +## A Bijective Internal Name Mangling Proposal + +FIR has a flat namespace. No two objects may have the same name. This +necessitates that we deploy some sort of name mangling scheme to unique +symbols from the front-end into FIR. + +We also want to be able to reverse-mangle these names back to rederive +their Fortran names. + +Fortran is case insensitive, which allows the compiler to convert the +user's identifiers to all lower case. Such a universal conversion implies +that all upper case letters are available for use in mangling. + +### Prefix `_Q` + +All mangled names have the prefix sequence `_Q` to indicate the name has +been mangled. (Q is chosen because it is a +[low frequency letter](http://pi.math.cornell.edu/~mec/2003-2004/cryptography/subs/frequencies.html) +in English.) + +### Scope Building + +Symbols can be scoped by the module, submodule, or procedure that contains +that symbol. After the `_Q` sigil, names are constructed from outermost to +innermost scope as + + * Module name prefixed with `M` + * Submodule name prefixed with `S` + * Procedure name prefixed with `F` + +Given: +``` + submodule (mod:s1mod) s2mod + ... + subroutine sub + ... + contains + function fun +``` + +The mangled name of `fun` becomes: +``` + _QMmodSs1modSs2modFsubPfun +``` + +### Common blocks + + * A common block name will be prefixed with `B` + +### Module scope global data + + * A global data entity is prefixed with `E` + * A global entity that is constant will be prefixed with `EC` + +### Procedures + + * A procedure is prefixed with `P` + +Given: +``` + subroutine sub +``` +The mangled name of `sub` becomes: +``` + _QPsub +``` + +### Derived types and related + + * A derived type is prefixed with `T` + * If a derived type has KIND parameters, they are listed in a consistent + canonical order where each takes the form `Ki` and where _i_ is the + compile-time constant value. (All type parameters are integer.) If _i_ + is a negative value, the prefix `KN` will be used and _i_ will reflect + the magnitude of the value. + +Given: +``` + module mymodule + type mytype + integer :: member + end type + ... +``` +The mangled name of `mytype` becomes: +``` + _QMmymoduleTmytype +``` + +Given: +``` + type yourtype(k1,k2) + integer, kind :: k1, k2 + real :: mem1 + complex :: mem2 + end type +``` + +The mangled name of `yourtype` where `k1=4` and `k2=-6` (at compile-time): +``` + _QTyourtypeK4KN6 +``` + + * A derived type dispatch table is prefixed with `D`. The dispatch table + for `type t` would be `_QDTt` + * A type descriptor instance is prefixed with `C`. Intrinsic types can + be encoded with their names and kinds. The type descriptor for the + type `yourtype` above would be `_QCTyourtypeK4KN6`. The type + descriptor for `REAL(4)` would be `_QCrealK4`. + +### Compiler generated names + +Compiler generated names do not have to be mapped back to Fortran. These +names will be prefixed with `_QQ` and followed by a unique compiler +generated identifier. diff --git a/documentation/FIRLangRef.md b/documentation/FIRLangRef.md index 7d8216dd5e7d..759fd772b629 100644 --- a/documentation/FIRLangRef.md +++ b/documentation/FIRLangRef.md @@ -112,6 +112,11 @@ Examples: !fir.array<*:f64> ; array of unknown rank and shape ``` +Unlike Fortran, FIR arrays have a starting index of 0 (always). This must be +accounted for when lowering Fortran arrays. See `fir.coordinate_of` and +`fir.embox` on how access maps can be used. (A starting index of 0 makes FIR +array offset computation consistent with tuples, record types, etc.) + ### Pointer-like Types
!fir.ref<ref-to-type>
@@ -133,6 +138,8 @@ value. This introduces aliasing (which may be important to the optimizer), and having a separate type allows for some refinement in alias analysis. _ptr-to-type_ cannot be `!fir.ptr`, `!fir.heap`, or `!fir.ref`. +A value of type `!fir.ptr` can introduce aliasing (unlike `!fir.ref`). +
!fir.heap<heap-to-type>
@@ -660,8 +667,15 @@ The content and type of _host-context_ is to be determined. Syntax: fir.coordinate_of box-or-ref-value , index-field-list : ( reference-like-type ) -> !fir.ref<T> -Compute the internal coordinate address starting from a boxed value or -unboxed memory reference. Returns a memory reference. +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. Example: @@ -674,9 +688,12 @@ Example: Syntax: fir.extract_value entity , index-field-list : ( entity-type , index-type ) -> subobject-type -Extract a value from an entity with a type composed of arrays and/or +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. +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. Example: @@ -686,73 +703,81 @@ Example: %61 = fir.extract_value %60, %59 : (!fir.type, !fir.field) -> i32 ``` -#### `fir.field_index` +#### `fir.insert_value` -Syntax: fir.field_index ("field-name") : !fir.field +Syntax: fir.insert_value entity , value , index-field-list : ( entity-type , value-type , index-field-type-list ) -> entity-type -Compute the field offset of a particular named field in a derived -type. Note: it is possible in Fortran to write code that can only determine -the exact offset of a particular field in a parameterized derived type at -runtime. +Insert a value into an entity with a type composed of tuples, arrays, and/or +derived types. Returns a new value of the same type as _entity_. It 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. Example: ```mlir - %62 = fir.field_index ("member_1") : !fir.field + %64 = fir.field_index("field") : !fir.field + %65 = fir.call @foo2() : () -> i32 + %66 = fir.call @foo3() : () -> !fir.type + %67 = fir.insert_value(%66, %65, %64) : (!fir.type, i32, !fir.field) -> !fir.type ``` -#### `fir.gendims` +The above is one possible translation of the following Fortran code sequence. -Syntax: fir.gendims triple-list : ( type-list ) -> !fir.dims<R> +```Fortran + temp1 = foo2() + temp2 = foo3() + temp2%field = temp1 +``` -Generate dimension information. This is needed to embox array entities. +#### `fir.field_index` + +Syntax: fir.field_index ("field-name") : !fir.field +Compute the field offset of a particular named field in a derived +type. Note: it is possible in Fortran to write code that can only determine +the exact offset of a particular field in a parameterized derived type at +runtime. Example: ```mlir - %c1 = constant 1 : i32 - %c10 = constant 10 : i32 - %63 = fir.gendims %c1,%c10,%c1 : (i32,i32,i32) -> !fir.dims<1> + %62 = fir.field_index ("member_1") : !fir.field ``` -#### `fir.insert_value` +#### `fir.len_param_index` -Syntax: fir.insert_value entity , value , index-field-list : ( entity-type , value-type , index-field-type-list ) -> entity-type +Syntax: fir.len_param_index ("len-param-name") : !fir.field -Insert a value into an entity with a type composed arrays and/or derived -types. Returns a new value of the same type as _entity_. +Compute the LEN type parameter offset of a particular named parameter in a +derived type. Example: ```mlir - %64 = fir.field_index("field") : !fir.field - %65 = fir.call @foo2() : () -> i32 - %66 = fir.call @foo3() : () -> !fir.type - %67 = fir.insert_value(%66, %65, %64) : (!fir.type, i32, !fir.field) -> !fir.type + %62 = fir.len_param_index("param_1") : !fir.field ``` -The above is a possible translation of the following Fortran code sequence. - -```Fortran - temp1 = foo2() - temp2 = foo3() - temp2%field = temp1 -``` +#### `fir.gendims` +Syntax: fir.gendims triple-list : ( type-list ) -> !fir.dims<R> -#### `fir.len_param_index` -Syntax: fir.len_param_index ("len-param-name") : !fir.field +Generate dimension information. This is needed to embox array entities. A +triple corresponds to the definition of Fortran's array slice operator. +Specifically, the components are `(first, last, stride)`. -Compute the LEN type parameter offset of a particular named parameter in a -derived type. +(These values will be lowered appropriately for the target runtime, which may +encode the triple as a `CFI_dim_t`, for example.) Example: ```mlir - %62 = fir.len_param_index("param_1") : !fir.field + %c1 = constant 1 : i32 + %c10 = constant 10 : i32 + %63 = fir.gendims %c1,%c10,%c1 : (i32,i32,i32) -> !fir.dims<1> ``` ### Generalized Control Flow Ops diff --git a/include/fir/Attribute.h b/include/fir/Attribute.h index c71a8862c4d6..9299774dd102 100644 --- a/include/fir/Attribute.h +++ b/include/fir/Attribute.h @@ -12,11 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIR_ATTRIBUTE_H -#define FIR_ATTRIBUTE_H +#ifndef DIALECT_FIR_FIRATTRIBUTE_H +#define DIALECT_FIR_FIRATTRIBUTE_H #include "mlir/IR/Attributes.h" +namespace mlir { +class DialectAsmParser; +class DialectAsmPrinter; +} // namespace mlir + namespace fir { class FIROpsDialect; @@ -33,6 +38,7 @@ enum AttributeKind { FIR_CLOSEDCLOSED_INTERVAL, FIR_OPENCLOSED_INTERVAL, FIR_CLOSEDOPEN_INTERVAL, + FIR_REAL_ATTR, }; class ExactTypeAttr @@ -114,10 +120,23 @@ class PointIntervalAttr : public mlir::Attribute::AttrBase { constexpr static unsigned getId() { return AttributeKind::FIR_POINT; } }; +class RealAttr : public mlir::Attribute::AttrBase { +public: + using Base::Base; + + static llvm::StringRef getAttrName() { return "real"; } + static RealAttr get(mlir::MLIRContext *ctxt, const llvm::APFloat &flt); + constexpr static bool kindof(unsigned kind) { return kind == getId(); } + constexpr static unsigned getId() { return AttributeKind::FIR_REAL_ATTR; } +}; + mlir::Attribute parseFirAttribute(FIROpsDialect *dialect, - llvm::StringRef rawText, mlir::Type type, - mlir::Location loc); + mlir::DialectAsmParser &parser, + mlir::Type type); + +void printFirAttribute(FIROpsDialect *dialect, mlir::Attribute attr, + mlir::DialectAsmPrinter &p); } // namespace fir -#endif // FIR_ATTRIBUTE_H +#endif // DIALECT_FIR_FIRATTRIBUTE_H diff --git a/include/fir/Dialect.h b/include/fir/FIRDialect.h similarity index 70% rename from include/fir/Dialect.h rename to include/fir/FIRDialect.h index d4a180cecce9..708b81bdadc9 100644 --- a/include/fir/Dialect.h +++ b/include/fir/FIRDialect.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIR_DIALECT_H -#define FIR_DIALECT_H +#ifndef DIALECT_FIR_FIRDIALECT_H +#define DIALECT_FIR_FIRDIALECT_H #include "mlir/IR/Dialect.h" @@ -24,6 +24,8 @@ class StringRef; namespace mlir { class Attribute; +class DialectAsmParser; +class DialectAsmPrinter; class Location; class MLIRContext; class Type; @@ -39,16 +41,15 @@ class FIROpsDialect final : public mlir::Dialect { static llvm::StringRef getDialectNamespace() { return "fir"; } - mlir::Type parseType(llvm::StringRef rawData, - mlir::Location loc) const override; - void printType(mlir::Type ty, llvm::raw_ostream &os) const override; + mlir::Type parseType(mlir::DialectAsmParser &parser) const override; + void printType(mlir::Type ty, mlir::DialectAsmPrinter &p) const override; - mlir::Attribute parseAttribute(llvm::StringRef rawText, mlir::Type type, - mlir::Location loc) const override; + mlir::Attribute parseAttribute(mlir::DialectAsmParser &parser, + mlir::Type type) const override; void printAttribute(mlir::Attribute attr, - llvm::raw_ostream &os) const override; + mlir::DialectAsmPrinter &p) const override; }; } // namespace fir -#endif // FIR_DIALECT_H +#endif // DIALECT_FIR_FIRDIALECT_H diff --git a/include/fir/FIROps.h b/include/fir/FIROps.h index 3a9bbf99af04..37938137567c 100644 --- a/include/fir/FIROps.h +++ b/include/fir/FIROps.h @@ -28,6 +28,34 @@ 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 +}; + /// `fir.global` is a typed symbol with an optional list of initializers. class GlobalOp : public mlir::Op< @@ -97,6 +125,15 @@ mlir::ParseResult parseSelector(mlir::OpAsmParser *parser, mlir::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); +mlir::ParseResult parseCmpfOp(mlir::OpAsmParser &parser, + mlir::OperationState &result); +mlir::ParseResult parseCmpcOp(mlir::OpAsmParser &parser, + mlir::OperationState &result); + #define GET_OP_CLASSES #include "fir/FIROps.h.inc" diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index 9d7bbcc9ac5d..f13d626baae1 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -87,18 +87,22 @@ def fir_DimsType : Type()">, "dim type">; def fir_TypeDescType : Type()">, "type desc type">; def fir_FieldType : Type()">, "field type">; +def fir_LenType : Type()">, + "LEN parameter type">; -def AnyCoordinateLike : TypeConstraint, "any coordinate index">; +def AnyComponentType : Type; + +def AnyCoordinateLike : TypeConstraint, "any coordinate index">; def AnyCoordinateType : Type; class fir_Op traits> : Op; -def SimplePrettyParserPrinter { -} - class fir_SimpleOp traits> : fir_Op { let parser = [{ @@ -179,23 +183,36 @@ class fir_AllocatableBaseOp traits = []> : class fir_AllocatableOp traits =[]> : fir_AllocatableBaseOp, fir_TwoBuilders, - Arguments<(ins TypeAttr:$in_type, Variadic:$shape)> { + Arguments<(ins TypeAttr:$in_type, Variadic:$args)> { + let parser = [{ M::Type intype; if (parser.parseType(intype)) return M::failure(); auto &builder = parser.getBuilder(); result.addAttribute("in_type", M::TypeAttr::get(intype)); - if (!parser.parseOptionalComma()) { - llvm::SmallVector typeVec; - llvm::SmallVector operands; + L::SmallVector operands; + L::SmallVector typeVec; + bool hasOperands = false; + if (!parser.parseOptionalLParen()) { if (parser.parseOperandList(operands, M::OpAsmParser::Delimiter::None) || - parser.resolveOperands(operands, builder.getIndexType(), - parser.getNameLoc(), result.operands)) + parser.parseRParen()) + return M::failure(); + auto lens = builder.getI32IntegerAttr(operands.size()); + result.addAttribute(lenpName(), lens); + hasOperands = true; + } + if (!parser.parseOptionalComma()) { + if (parser.parseOperandList(operands, M::OpAsmParser::Delimiter::None)) return M::failure(); + hasOperands = true; } + if (hasOperands && + parser.resolveOperands(operands, builder.getIndexType(), + parser.getNameLoc(), result.operands)) + return M::failure(); M::Type restype; - if (parser.parseOptionalAttributeDict(result.attributes) || + if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColonType(restype) || parser.addTypeToList(restype, result.types)) return M::failure(); @@ -204,13 +221,36 @@ class fir_AllocatableOp traits =[]> : let printer = [{ p << getOperationName() << ' ' << getAttr("in_type"); + if (hasLenParams()) { + p << '('; + p.printOperands(getLenParams()); + p << ')'; + } for (auto sh : getShapeOperands()) { p << ", "; p.printOperand(sh); } - p.printOptionalAttrDict(getAttrs(), {"in_type"}); + p.printOptionalAttrDict(getAttrs(), {"in_type", lenpName()}); p << " : " << getType(); }]; + + let extraClassDeclaration = [{ + constexpr static 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); + }]; } // Memory SSA operations @@ -237,14 +277,6 @@ def fir_AllocaOp : fir_AllocatableOp<"alloca"> { } return M::success(); }]; - - let extraClassDeclaration = [{ - mlir::Type getAllocatedType(); - operand_range getShapeOperands() { - return {operand_begin(), operand_end()}; - } - static mlir::Type getRefTy(mlir::Type ty); - }]; } def fir_LoadOp : fir_OneResultOp<"load", []>, @@ -270,7 +302,7 @@ def fir_LoadOp : fir_OneResultOp<"load", []>, M::Type type; M::OpAsmParser::OperandType oper; if (parser.parseOperand(oper) || - parser.parseOptionalAttributeDict(result.attributes) || + parser.parseOptionalAttrDict(result.attributes) || parser.parseColonType(type) || parser.resolveOperand(oper, type, result.operands)) return M::failure(); @@ -310,7 +342,7 @@ def fir_StoreOp : fir_Op<"store", []>, if (parser.parseOperand(oper) || parser.parseKeyword("to") || parser.parseOperand(store) || - parser.parseOptionalAttributeDict(result.attributes) || + parser.parseOptionalAttrDict(result.attributes) || parser.parseColonType(type) || parser.resolveOperand(oper, elementType(type), result.operands) || @@ -380,14 +412,6 @@ def fir_AllocMemOp : fir_AllocatableOp<"allocmem"> { } return M::success(); }]; - - let extraClassDeclaration = [{ - mlir::Type getAllocatedType(); - operand_range getShapeOperands() { - return {operand_begin(), operand_end()}; - } - static mlir::Type getRefTy(mlir::Type ty); - }]; } def fir_FreeMemOp : fir_Op<"freemem", []>, @@ -405,7 +429,7 @@ def fir_FreeMemOp : fir_Op<"freemem", []>, M::Type type; M::OpAsmParser::OperandType oper; if (parser.parseOperand(oper) || - parser.parseOptionalAttributeDict(result.attributes) || + parser.parseOptionalAttrDict(result.attributes) || parser.parseColonType(type) || parser.resolveOperand(oper, type, result.operands)) return M::failure(); @@ -784,7 +808,7 @@ def fir_FirEndOp : fir_Op<"end", [Terminator]> { // Operations on !fir.box type objects def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]>, Results<(outs fir_BoxType)>, - Arguments<(ins AnyReferenceLike:$memref, Variadic:$dims)> { + Arguments<(ins AnyReferenceLike:$memref, Variadic:$args)> { let summary = "boxes a given reference and (optional) dimension information"; let description = [{ @@ -795,11 +819,19 @@ def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]>, Results<(outs fir_BoxType)>, let parser = [{ M::FunctionType type; - llvm::SmallVector operands; + llvm::SmallVector operands; M::OpAsmParser::OperandType memref; if (parser.parseOperand(memref)) return M::failure(); operands.push_back(memref); + auto &builder = parser.getBuilder(); + if (!parser.parseOptionalLParen()) { + if (parser.parseOperandList(operands, M::OpAsmParser::Delimiter::None) || + parser.parseRParen()) + return M::failure(); + auto lens = builder.getI32IntegerAttr(operands.size()); + result.addAttribute(lenpName(), lens); + } if (!parser.parseOptionalComma()) { M::OpAsmParser::OperandType dims; if (parser.parseOperand(dims)) @@ -822,6 +854,11 @@ def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]>, Results<(outs fir_BoxType)>, let printer = [{ p << getOperationName() << ' '; p.printOperand(memref()); + if (hasLenParams()) { + p << '('; + p.printOperands(getLenParams()); + p << ')'; + } if (getNumOperands() == 2) { p << ", "; p.printOperands(dims()); @@ -831,6 +868,22 @@ def fir_EmboxOp : fir_Op<"embox", [NoSideEffect]>, Results<(outs fir_BoxType)>, p << " : "; p.printFunctionalType(getOperation()); }]; + + let extraClassDeclaration = [{ + constexpr static 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(), operand_end()}; + } + }]; } def fir_EmboxCharOp : fir_Op<"emboxchar", [NoSideEffect]>, @@ -923,8 +976,8 @@ def fir_EmboxProcOp : fir_Op<"emboxproc", [NoSideEffect]>, } def fir_UnboxOp : fir_SimpleOp<"unbox", [NoSideEffect]>, - Results<(outs fir_ReferenceType, AnyInteger, AnyInteger, fir_TypeDescType, - AnyInteger, fir_DimsType)>, + Results<(outs fir_ReferenceType, AnyIntegerLike, AnyIntegerLike, + fir_TypeDescType, AnyIntegerLike, fir_DimsType)>, Arguments<(ins fir_BoxType:$box)> { let summary = "unbox the boxed value into a tuple value"; @@ -935,7 +988,7 @@ def fir_UnboxOp : fir_SimpleOp<"unbox", [NoSideEffect]>, } def fir_UnboxCharOp : fir_SimpleOp<"unboxchar", [NoSideEffect]>, - Results<(outs fir_ReferenceType, AnyInteger)>, + Results<(outs fir_ReferenceType, AnyIntegerLike)>, Arguments<(ins fir_BoxCharType:$boxchar)> { let summary = "unbox a boxchar value into a pair value"; @@ -987,7 +1040,7 @@ def fir_BoxAddrOp : fir_SimpleOneResultOp<"box_addr", [NoSideEffect]>, } def fir_BoxCharLenOp : fir_SimpleOp<"boxchar_len", [NoSideEffect]>, - Results<(outs AnyInteger)>, Arguments<(ins fir_BoxCharType:$val)> { + Results<(outs AnyIntegerLike)>, Arguments<(ins fir_BoxCharType:$val)> { let summary = "return the LEN type parameter from a boxchar value"; let description = [{ @@ -1028,6 +1081,10 @@ def fir_BoxDimsOp : fir_Op<"box_dims", [NoSideEffect]>, p << " : "; p.printFunctionalType(getOperation()); }]; + + let extraClassDeclaration = [{ + mlir::Type getTupleType(); + }]; } def fir_BoxEleSizeOp : fir_SimpleOneResultOp<"box_elesize", [NoSideEffect]>, @@ -1141,10 +1198,23 @@ def fir_CoordinateOp : fir_Op<"coordinate_of", [NoSideEffect]>, p << " : "; p.printFunctionalType(getOperation()); }]; + + 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 M::success(); + }]; } def fir_ExtractValueOp : fir_OneResultOp<"extract_value", [NoSideEffect]>, - Arguments<(ins AnyCompositeLike:$adt, Variadic:$coor)> { + Arguments<(ins AnyCompositeLike:$adt, Variadic:$coor)> { let summary = "Extract a value from an aggregate SSA-value"; let description = [{ @@ -1175,7 +1245,9 @@ def fir_ExtractValueOp : fir_OneResultOp<"extract_value", [NoSideEffect]>, }]; } -def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoSideEffect]> { +def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoSideEffect]>, + Arguments<(ins StrAttr:$field_id, TypeAttr:$on_type, + Variadic:$lenparams)> { let summary = "create a field index value from a field identifier"; let description = [{ @@ -1187,43 +1259,71 @@ def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoSideEffect]> { subobjects. }]; - let arguments = (ins StrAttr:$field_id); - let builders = [OpBuilder< - "Builder *builder, OperationState &result, StringRef fieldName", + "Builder *builder, OperationState &result, StringRef fieldName," + "Type recTy, ArrayRef operands = {}", [{ - result.addAttribute("field_id", builder->getStringAttr(fieldName)); + result.addAttribute(fieldAttrName(), builder->getStringAttr(fieldName)); + result.addAttribute(typeAttrName(), TypeAttr::get(recTy)); + result.addOperands(operands); }] >]; let parser = [{ - if (parser.parseLParen()) - return M::failure(); llvm::StringRef fieldName; - if (parser.parseOptionalKeyword(&fieldName)) { - M::StringAttr attr; - if (parser.parseAttribute(attr, "field_id", result.attributes)) + auto &builder = parser.getBuilder(); + M::Type recty; + if (parser.parseOptionalKeyword(&fieldName) || + parser.parseComma() || + parser.parseType(recty)) + return M::failure(); + result.addAttribute(fieldAttrName(), builder.getStringAttr(fieldName)); + if (!recty.dyn_cast()) + return M::failure(); + result.addAttribute(typeAttrName(), M::TypeAttr::get(recty)); + if (!parser.parseLParen()) { + L::SmallVector operands; + L::SmallVector types; + auto loc = parser.getNameLoc(); + if (parser.parseOperandList(operands, M::OpAsmParser::Delimiter::None) || + parser.parseRParen() || + parser.parseColonTypeList(types) || + parser.resolveOperands(operands, types, loc, result.operands)) return M::failure(); - } else { - result.addAttribute("field_id", - parser.getBuilder().getStringAttr(fieldName)); } - M::Type type; - if (parser.parseRParen() || - parser.parseColonType(type) || - parser.addTypeToList(type, result.types)) + M::Type fieldType = fir::FieldType::get(builder.getContext()); + if (parser.addTypeToList(fieldType, result.types)) return M::failure(); return M::success(); }]; let printer = [{ - p << getOperationName() << " (" << getAttr("field_id") << ") : " - << getType(); + 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 extraClassDeclaration = [{ + constexpr static llvm::StringRef fieldAttrName() { return "field_id"; } + constexpr static llvm::StringRef typeAttrName() { return "on_type"; } }]; } def fir_GenDimsOp : fir_OneResultOp<"gendims", [NoSideEffect]>, - Arguments<(ins Variadic:$triples)> { + Arguments<(ins Variadic:$triples)> { let summary = "generate a value of type `!fir.dims`"; let description = [{ @@ -1259,7 +1359,7 @@ def fir_GenDimsOp : fir_OneResultOp<"gendims", [NoSideEffect]>, def fir_InsertValueOp : fir_OneResultOp<"insert_value", [NoSideEffect]>, Arguments<(ins AnyCompositeLike:$adt, AnyType:$val, - Variadic:$coor)> { + Variadic:$coor)> { let summary = "insert a new sub-value into a copy of an existing aggregate"; let description = [{ @@ -1289,51 +1389,56 @@ def fir_InsertValueOp : fir_OneResultOp<"insert_value", [NoSideEffect]>, }]; } -def fir_LenParamIndexOp : fir_OneResultOp<"len_param_index", [NoSideEffect]> { +def fir_LenParamIndexOp : fir_OneResultOp<"len_param_index", [NoSideEffect]>, + Arguments<(ins StrAttr:$field_id, TypeAttr:$on_type)> { let summary = "create a field index value from a LEN type parameter identifier"; let description = [{ - Generate a field (offset) value from an LEN parameter 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. + 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. }]; - let arguments = (ins StrAttr:$field_id); - let builders = [OpBuilder< - "Builder *builder, OperationState &result, StringRef fieldName", + "Builder *builder, OperationState &result, StringRef fieldName, Type recTy", [{ - result.addAttribute("field_id", builder->getStringAttr(fieldName)); + result.addAttribute(fieldAttrName(), builder->getStringAttr(fieldName)); + result.addAttribute(typeAttrName(), TypeAttr::get(recTy)); }] >]; let parser = [{ - if (parser.parseLParen()) - return M::failure(); llvm::StringRef fieldName; - if (parser.parseOptionalKeyword(&fieldName)) { - M::StringAttr attr; - if (parser.parseAttribute(attr, "field_id", result.attributes)) - return M::failure(); - } else { - result.addAttribute("field_id", - parser.getBuilder().getStringAttr(fieldName)); - } - M::Type type; - if (parser.parseRParen() || - parser.parseColonType(type) || - parser.addTypeToList(type, result.types)) + auto &builder = parser.getBuilder(); + M::Type recty; + if (parser.parseOptionalKeyword(&fieldName) || + parser.parseComma() || + parser.parseType(recty)) + return M::failure(); + result.addAttribute(fieldAttrName(), builder.getStringAttr(fieldName)); + if (!recty.dyn_cast()) + return M::failure(); + result.addAttribute(typeAttrName(), M::TypeAttr::get(recty)); + M::Type lenType = fir::LenType::get(builder.getContext()); + if (parser.addTypeToList(lenType, result.types)) return M::failure(); return M::success(); }]; let printer = [{ - p << getOperationName() << " (" << getAttr("field_id") << ") : " - << getType(); + p << getOperationName() << ' ' + << getAttrOfType(fieldAttrName()).getValue() << ", " + << getAttr(typeAttrName()); + }]; + + let extraClassDeclaration = [{ + constexpr static llvm::StringRef fieldAttrName() { return "field_id"; } + constexpr static llvm::StringRef typeAttrName() { return "on_type"; } + mlir::Type getOnType() { + return getAttrOfType(typeAttrName()).getValue(); + } }]; } @@ -1348,7 +1453,7 @@ def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> { }]; let arguments = (ins Index:$lowerBound, Index:$upperBound, Variadic:$optstep, OptionalAttr:$constep, - OptionalAttr:$unordered); + OptionalAttr:$unordered); let regions = (region SizedRegion<1>:$region); let skipDefaultBuilders = 1; let builders = [ @@ -1379,10 +1484,9 @@ def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> { auto step = optstep(); if (step.begin() != step.end()) { auto *s = (*step.begin())->getDefiningOp(); - if (auto cst = dyn_cast_or_null(s)) { + 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 @@ -1510,7 +1614,7 @@ def fir_DispatchOp : fir_Op<"dispatch", []>, parser.getBuilder().getStringAttr(calleeName)); } if (parser.parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || - parser.parseOptionalAttributeDict(result.attributes) || + parser.parseOptionalAttrDict(result.attributes) || parser.parseColonType(calleeType) || parser.addTypesToList(calleeType.getResults(), result.types) || parser.resolveOperands( @@ -1546,23 +1650,68 @@ def fir_DispatchOp : fir_Op<"dispatch", []>, }]; } +// Constant operations that support Fortran + +def fir_ConstantOp : fir_Op<"constant", [NoSideEffect]>, + Results<(outs AnyType)> { + let summary = "create a constant of FIR type"; + + let description = [{ + An FIR constant + }]; + + let parser = [{ + M::StringAttr attr; + if (parser.parseAttribute(attr, value(), result.attributes) || + parser.addTypesToList(attr.getType(), result.types)) + return M::failure(); + return M::success(); + }]; + + let printer = [{ + p << getOperationName() << ' ' << getAttr(value()); + }]; + + //let verifier = [{ + // FIXME! + //}]; + + let extraClassDeclaration = [{ + constexpr static char const *value() { return "value"; } + mlir::Attribute getValue() { return getAttr(value()); } + }]; +} + // Complex operations class fir_ArithmeticOp traits = []> : fir_Op { + !listconcat(traits, [NoSideEffect, SameOperandsAndResultType])>, + Results<(outs AnyType)> { + let parser = [{ + return impl::parseOneResultSameOperandTypeOp(parser, result); + }]; - let results = (outs AnyType); + 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::printComplexBinaryOp(this->getOperation(), p); - }]; + let printer = [{ return fir::printUnaryOp(this->getOperation(), p); }]; } +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)>; @@ -1572,6 +1721,46 @@ 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]>, + Results<(outs AnyLogicalLike)>, + Arguments<(ins AnyRealLike:$lhs, AnyRealLike:$rhs)> { + 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 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 = [{ + constexpr static llvm::StringRef getPredicateAttrName() { + return "predicate"; + } + static CmpFPredicate getPredicateByName(llvm::StringRef name); + + CmpFPredicate getPredicate() { + return (CmpFPredicate)getAttrOfType( + getPredicateAttrName()).getInt(); + } + }]; +} + +class ComplexUnaryArithmeticOp traits = []> : + fir_UnaryArithmeticOp, + Arguments<(ins fir_ComplexType:$operand)>; + +def fir_NegcOp : ComplexUnaryArithmeticOp<"negc">; class ComplexArithmeticOp traits = []> : fir_ArithmeticOp, @@ -1581,6 +1770,38 @@ 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]>, + Results<(outs AnyLogicalLike)>, + Arguments<(ins fir_ComplexType:$lhs, fir_ComplexType:$rhs)> { + let summary = "complex floating-point comparison operator"; + + let description = [{ + A complex comparison to handle complex types found in FIR. + }]; + + let builders = [OpBuilder< + "Builder *builder, OperationState &result, CmpFPredicate predicate," + "Value *lhs, Value *rhs", [{ + fir::buildCmpCOp(builder, result, predicate, lhs, rhs); + }]>]; + + let parser = [{ return fir::parseCmpcOp(parser, result); }]; + let printer = [{ fir::printCmpcOp(p, *this); }]; + + let extraClassDeclaration = [{ + constexpr static llvm::StringRef getPredicateAttrName() { + return "predicate"; + } + + CmpFPredicate getPredicate() { + return (CmpFPredicate)getAttrOfType( + getPredicateAttrName()).getInt(); + } + }]; +} // Other misc. operations @@ -1600,7 +1821,7 @@ def fir_AddrOfOp : fir_OneResultOp<"address_of", [NoSideEffect]> { if (parser.parseLParen() || parser.parseAttribute(attr, "symbol", result.attributes) || parser.parseRParen() || - parser.parseOptionalAttributeDict(result.attributes) || + parser.parseOptionalAttrDict(result.attributes) || parser.parseColonType(type) || parser.addTypeToList(type, result.types)) return M::failure(); @@ -1627,7 +1848,7 @@ def fir_ConvertOp : fir_OneResultOp<"convert", [NoSideEffect]>, llvm::SmallVector operands; if (parser.parseOperandList(operands, 1, M::OpAsmParser::Delimiter::None) || - parser.parseOptionalAttributeDict(result.attributes) || + parser.parseOptionalAttrDict(result.attributes) || parser.parseColonType(type) || parser.resolveOperands(operands, type.getInputs(), parser.getNameLoc(), result.operands) || @@ -1664,11 +1885,18 @@ def fir_GenTypeDescOp : fir_OneResultOp<"gentypedesc", [NoSideEffect]> { return M::failure(); return M::success(); }]; + let printer = [{ p << getOperationName() << ' ' << getAttr("in_type"); p << " : " << getType(); p.printOptionalAttrDict(getAttrs(), {"in_type"}); }]; + + let extraClassDeclaration = [{ + mlir::Type getInType() { + return getAttrOfType("in_type").getType(); + } + }]; } def fir_NoReassocOp : fir_OneResultOp<"no_reassoc", @@ -1722,7 +1950,7 @@ def fir_GlobalEntryOp : fir_Op<"global_entry", []>, } if (parser.parseComma() || parser.parseOperand(constant) || - parser.parseOptionalAttributeDict(result.attributes) || + parser.parseOptionalAttrDict(result.attributes) || parser.parseColonType(type) || parser.resolveOperand(constant, type, result.operands)) return M::failure(); diff --git a/include/fir/FIROpsSupport.h b/include/fir/FIROpsSupport.h index ee7c6b4cb85b..a0f48de4c69e 100644 --- a/include/fir/FIROpsSupport.h +++ b/include/fir/FIROpsSupport.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIR_FIROPSSUPPORT_H -#define FIR_FIROPSSUPPORT_H +#ifndef FIR_FIROPS_SUPPORT_H +#define FIR_FIROPS_SUPPORT_H #include "fir/FIROps.h" #include "mlir/Dialect/StandardOps/Ops.h" @@ -23,32 +23,44 @@ namespace fir { /// return true iff the Operation is a non-volatile LoadOp inline bool nonVolatileLoad(mlir::Operation *op) { if (auto load = dyn_cast(op)) - if (!load.getAttr("volatile")) - return true; + 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)) { - if (!call.getAttr("pure")) - return true; - } else if (auto dispatch = dyn_cast(op)) { - if (!dispatch.getAttr("pure")) - return true; - } else if (auto call = dyn_cast(op)) { - if (!call.getAttr("pure")) - return true; - } else if (auto icall = dyn_cast(op)) { - if (!icall.getAttr("pure")) - return true; - } + 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; } } // namespace fir -#endif // FIR_FIROPSSUPPORT_H +#endif // FIR_FIROPS_SUPPORT_H diff --git a/include/fir/Type.h b/include/fir/FIRType.h similarity index 90% rename from include/fir/Type.h rename to include/fir/FIRType.h index 8a469c4d35b8..a5161c3afa03 100644 --- a/include/fir/Type.h +++ b/include/fir/FIRType.h @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIR_TYPE_H -#define FIR_TYPE_H +#ifndef DIALECT_FIR_FIRTYPE_H +#define DIALECT_FIR_FIRTYPE_H #include "mlir/IR/Attributes.h" #include "mlir/IR/Types.h" -#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" namespace llvm { class raw_ostream; @@ -27,6 +27,11 @@ class ArrayRef; class hash_code; } // namespace llvm +namespace mlir { +class DialectAsmParser; +class DialectAsmPrinter; +} // namespace mlir + namespace fir { class FIROpsDialect; @@ -43,6 +48,7 @@ struct DimsTypeStorage; struct FieldTypeStorage; struct HeapTypeStorage; struct IntTypeStorage; +struct LenTypeStorage; struct LogicalTypeStorage; struct PointerTypeStorage; struct RealTypeStorage; @@ -64,7 +70,8 @@ enum TypeKind { FIR_DIMS, FIR_FIELD, FIR_HEAP, - FIR_INT, // intrinsic + FIR_INT, // intrinsic + FIR_LEN, FIR_LOGICAL, // intrinsic FIR_POINTER, FIR_REAL, // intrinsic @@ -202,6 +209,14 @@ class HeapType : 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: @@ -235,14 +250,13 @@ class SequenceType : public mlir::Type::TypeBase { public: using Base::Base; - using BoundInfo = int64_t; - using Extent = llvm::Optional; - using Bounds = std::vector; - using Shape = llvm::Optional; + using Extent = int64_t; + using Shape = llvm::SmallVector; mlir::Type getEleTy() const; Shape getShape() const; mlir::AffineMapAttr getLayoutMap() const; + unsigned getDimension() const { return getShape().size(); } static SequenceType get(const Shape &shape, mlir::Type elementType, mlir::AffineMapAttr map = {}); @@ -283,6 +297,13 @@ class RecordType : public mlir::Type::TypeBase lenPList, llvm::ArrayRef typeList); @@ -297,17 +318,10 @@ class RecordType : public mlir::Type::TypeBase &ext) { - return fir::hash_value(ext); -} -} // namespace llvm - -#endif // FIR_TYPE_H +#endif // DIALECT_FIR_FIRTYPE_H diff --git a/include/fir/InternalNames.h b/include/fir/InternalNames.h new file mode 100644 index 000000000000..9f00d150cd53 --- /dev/null +++ b/include/fir/InternalNames.h @@ -0,0 +1,89 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIR_INTERNAL_NAMES_H +#define FIR_INTERNAL_NAMES_H + +#include "llvm/ADT/StringSet.h" +#include + +namespace llvm { +template +class ArrayRef; +template +class Optional; +class Twine; +} // namespace llvm + +namespace fir { + +/// Internal name mangling of identifiers +struct NameMangler { + enum class IntrinsicType { CHARACTER, COMPLEX, INTEGER, LOGICAL, REAL }; + + NameMangler(); + + /// Mangle a common block name + llvm::Twine doCommonBlock(llvm::StringRef name); + + /// Mangle a (global) constant name + llvm::Twine doConstant(llvm::ArrayRef modules, + llvm::StringRef name); + + /// Mangle a dispatch table name + llvm::Twine doDispatchTable(llvm::ArrayRef modules, + llvm::Optional host, + llvm::StringRef name, + llvm::ArrayRef kinds); + + /// Mangle a compiler generated name + llvm::Twine doGenerated(llvm::StringRef name); + + /// Mangle an intrinsic type descriptor + llvm::Twine doIntrinsicTypeDescriptor(llvm::ArrayRef modules, + llvm::Optional host, + IntrinsicType type, std::int64_t kind); + + /// Mangle a procedure name + llvm::Twine doProcedure(llvm::ArrayRef modules, + llvm::Optional host, + llvm::StringRef name); + + /// Mangle a derived type name + llvm::Twine doType(llvm::ArrayRef modules, + llvm::Optional host, llvm::StringRef name, + llvm::ArrayRef kinds); + + /// Mangle a (derived) type descriptor name + llvm::Twine doTypeDescriptor(llvm::ArrayRef modules, + llvm::Optional host, + llvm::StringRef name, + llvm::ArrayRef kinds); + + /// Mangle a (global) variable name + llvm::Twine doVariable(llvm::ArrayRef modules, + llvm::StringRef name); + +private: + llvm::Twine addAsString(std::int64_t i); + llvm::Twine doKind(std::int64_t kind); + llvm::Twine doKinds(llvm::ArrayRef kinds); + llvm::Twine toLower(llvm::StringRef name); + + llvm::StringSet<> cache; +}; + +} // namespace fir + +#endif // FIR_INTERNAL_NAMES_H diff --git a/include/fir/Tilikum/Tilikum.h b/include/fir/Tilikum/Tilikum.h index d08737ea80ba..dca126e0491e 100644 --- a/include/fir/Tilikum/Tilikum.h +++ b/include/fir/Tilikum/Tilikum.h @@ -17,6 +17,9 @@ #include +namespace llvm { +class StringRef; +} namespace mlir { class Pass; } @@ -27,7 +30,7 @@ namespace fir { std::unique_ptr createFIRToLLVMPass(); /// Convert the LLVM IR dialect to LLVM-IR proper -std::unique_ptr createLLVMDialectToLLVMPass(); +std::unique_ptr createLLVMDialectToLLVMPass(llvm::StringRef output); } // namespace fir diff --git a/include/fir/Transforms/Passes.h b/include/fir/Transforms/Passes.h index 64fd3bebefaf..faafc64608ed 100644 --- a/include/fir/Transforms/Passes.h +++ b/include/fir/Transforms/Passes.h @@ -28,6 +28,11 @@ namespace fir { /// Effects aware CSE pass std::unique_ptr> createCSEPass(); +/// Convert `fir.loop` and `fir.where` to `loop.for` and `loop.if`. This +/// conversion enables the `createLowerToCFGPass` to transform these to CFG +/// form. +std::unique_ptr> createFirToLoopPass(); + /// A pass to convert the FIR dialect from "Mem-SSA" form to "Reg-SSA" /// form. This pass is a port of LLVM's mem2reg pass, but modified for the FIR /// dialect as well as the restructuring of MLIR's representation to present PHI diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 0897ea911cf1..87922bfeba47 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -16,9 +16,9 @@ #include "builder.h" #include "convert-expr.h" #include "fe-helper.h" -#include "fir/Dialect.h" +#include "fir/FIRDialect.h" #include "fir/FIROps.h" -#include "fir/Type.h" +#include "fir/FIRType.h" #include "flattened.h" #include "intrinsics.h" #include "io.h" @@ -166,13 +166,13 @@ class FIRConverter { return {}; } M::Value *genGE(M::Value *lhs, M::Value *rhs) { - return genCompare(lhs, rhs); + return genCompare(lhs, rhs); } M::Value *genLE(M::Value *lhs, M::Value *rhs) { - return genCompare(lhs, rhs); + return genCompare(lhs, rhs); } M::Value *genEQ(M::Value *lhs, M::Value *rhs) { - return genCompare(lhs, rhs); + return genCompare(lhs, rhs); } M::Value *genAND(M::Value *lhs, M::Value *rhs) { return build().create(lhs->getLoc(), lhs, rhs); @@ -364,6 +364,8 @@ class FIRConverter { auto &fas{stmt.statement}; auto &ctrl{std::get>(fas.t).value()}; auto &bld{build()}; + (void)ctrl; + (void)bld; // FIXME // bld.create(); for (auto &s : std::get>(forall.t)) { genFIROnVariant(s); @@ -452,7 +454,7 @@ class FIRConverter { auto zero{build().create( load.getLoc(), build().getIntegerAttr(load.getType(), 0))}; auto cond{build().create( - load.getLoc(), M::CmpIPredicate::SGT, load, zero)}; + load.getLoc(), M::CmpIPredicate::sgt, load, zero)}; info->condition = cond; } } @@ -463,21 +465,23 @@ class FIRConverter { build().create(toLocation(), callee, operands); build().create(toLocation()); } + void genReturnStmt(AnalysisData &, const Pa::FunctionSubprogram &func) { auto &stmt{std::get>(func.t)}; auto &name{std::get(stmt.statement.t)}; assert(name.symbol); const auto &details{name.symbol->get()}; - const Se::Symbol *result{&details.result()}; - M::Value *resultRef{symbolMap.lookupSymbol(result)}; + M::Value *resultRef{symbolMap.lookupSymbol(details.result())}; assert(resultRef); // FIXME might die if result // was never referenced before and temp not created. M::Value *resultVal{build().create(toLocation(), resultRef)}; build().create(toLocation(), resultVal); } + void genReturnStmt(const Pa::MainProgram &) { build().create(toLocation()); } + void genReturnStmt( const Pa::SubroutineSubprogram &, const Pa::ReturnStmt * = nullptr) { // TODO use Pa::ReturnStmt for alternate return @@ -973,13 +977,15 @@ void FIRConverter::translateRoutine( llvm::SmallVector results; if (funcSym) { if (auto *details{funcSym->detailsIf()}) { - for (auto a : details->dummyArgs()) { - auto type{translateSymbolToFIRType(&mlirContext, defaults, a)}; - args.push_back(fir::ReferenceType::get(type)); + for (auto *a : details->dummyArgs()) { + if (a) { // nullptr indicates alternate return argument + auto type{translateSymbolToFIRType(&mlirContext, defaults, *a)}; + args.push_back(fir::ReferenceType::get(type)); + } } if (details->isFunction()) { // FIXME: handle subroutines that return magic values - auto *result{&details->result()}; + auto result{details->result()}; results.push_back( translateSymbolToFIRType(&mlirContext, defaults, result)); } @@ -998,9 +1004,14 @@ void FIRConverter::translateRoutine( if (funcSym) { auto *entryBlock{&func.front()}; if (auto *details{funcSym->detailsIf()}) { + // TODO zipping might be an issue in case of alternate returns for (const auto &v : llvm::zip(details->dummyArgs(), entryBlock->getArguments())) { - symbolMap.addSymbol(std::get<0>(v), std::get<1>(v)); + if (std::get<0>(v)) { + symbolMap.addSymbol(*std::get<0>(v), std::get<1>(v)); + } else { + TODO(); // handle alternate return, maybe nothing todo here though + } } } else { llvm::errs() << "Symbol: " << funcSym->name().ToString() << " @ " @@ -1015,8 +1026,6 @@ void FIRConverter::translateRoutine( finalizeQueued(); } -M::DialectRegistration FIROps; - } // namespace void Br::crossBurnsideBridge(BurnsideBridge &bridge, const Pa::Program &prg) { diff --git a/lib/burnside/builder.cc b/lib/burnside/builder.cc index 650f80c21790..409203483f2b 100644 --- a/lib/burnside/builder.cc +++ b/lib/burnside/builder.cc @@ -45,11 +45,11 @@ mlir::FuncOp B::getNamedFunction(mlir::ModuleOp module, llvm::StringRef name) { return module.lookupSymbol(name); } -void B::SymMap::addSymbol(const semantics::Symbol *symbol, mlir::Value *value) { - symbolMap.try_emplace(symbol, value); +void B::SymMap::addSymbol(semantics::SymbolRef symbol, mlir::Value *value) { + symbolMap.try_emplace(&*symbol, value); } -mlir::Value *B::SymMap::lookupSymbol(const semantics::Symbol *symbol) { - auto iter{symbolMap.find(symbol)}; +mlir::Value *B::SymMap::lookupSymbol(semantics::SymbolRef symbol) { + auto iter{symbolMap.find(&*symbol)}; return (iter == symbolMap.end()) ? nullptr : iter->second; } diff --git a/lib/burnside/builder.h b/lib/burnside/builder.h index a2bce6b79faa..563a13e0db6f 100644 --- a/lib/burnside/builder.h +++ b/lib/burnside/builder.h @@ -40,9 +40,9 @@ class SymMap { llvm::DenseMap symbolMap; public: - void addSymbol(const semantics::Symbol *symbol, mlir::Value *value); + void addSymbol(semantics::SymbolRef symbol, mlir::Value *value); - mlir::Value *lookupSymbol(const semantics::Symbol *symbol); + mlir::Value *lookupSymbol(semantics::SymbolRef symbol); }; std::string applyNameMangling(llvm::StringRef parserName); diff --git a/lib/burnside/complex.h b/lib/burnside/complex-handler.h similarity index 65% rename from lib/burnside/complex.h rename to lib/burnside/complex-handler.h index bdf874d6fbca..6c408c0e9879 100644 --- a/lib/burnside/complex.h +++ b/lib/burnside/complex-handler.h @@ -19,66 +19,66 @@ #include "fe-helper.h" #include "fir/FIROps.h" -#include "fir/Type.h" +#include "fir/FIRType.h" namespace Fortran::burnside { /// Provide helpers to generate Complex manipulations in FIR. class ComplexHandler { public: + // The values of part enum members are meaningful for + // InsertValueOp and ExtractValueOp so they are explicit. + enum class Part { Real = 0, Imag = 1 }; + ComplexHandler(mlir::OpBuilder &b, mlir::Location l) : builder{b}, loc{l} {} mlir::Type getComplexPartType(fir::KindTy complexKind) { return convertReal(builder.getContext(), complexKind); } + mlir::Value *createComplex( + fir::KindTy kind, mlir::Value *real, mlir::Value *imag) { + mlir::Type complexTy{fir::CplxType::get(builder.getContext(), kind)}; + mlir::Value *und{builder.create(loc, complexTy)}; + return insert(insert(und, real), imag); + } + + // Complex part manipulation helpers mlir::Type getComplexPartType(mlir::Type complexType) { return getComplexPartType(complexType.cast().getFKind()); } - mlir::Type getComplexPartType(mlir::Value *cplx) { assert(cplx != nullptr); return getComplexPartType(cplx->getType()); } - mlir::Value *createComplexPart(mlir::Value *cplx, bool isImaginaryPart) { + template mlir::Value *extract(mlir::Value *cplx) { return builder.create( - loc, getComplexPartType(cplx), cplx, getPartId(isImaginaryPart)); - } - - mlir::Value *createComplexRealPart(mlir::Value *cplx) { - return createComplexPart(cplx, false); + loc, getComplexPartType(cplx), cplx, getPartId()); } - - mlir::Value *createComplexImagPart(mlir::Value *cplx) { - return createComplexPart(cplx, true); - } - - mlir::Value *setComplexPart( - mlir::Value *cplx, mlir::Value *part, bool isImaginaryPart) { + template + mlir::Value *insert(mlir::Value *cplx, mlir::Value *part) { assert(cplx != nullptr); return builder.create( - loc, cplx->getType(), cplx, part, getPartId(isImaginaryPart)); - } - mlir::Value *setRealPart(mlir::Value *cplx, mlir::Value *part) { - return setComplexPart(cplx, part, false); - } - mlir::Value *setImagPart(mlir::Value *cplx, mlir::Value *part) { - return setComplexPart(cplx, part, true); + loc, cplx->getType(), cplx, part, getPartId()); } - mlir::Value *createComplex( - fir::KindTy kind, mlir::Value *real, mlir::Value *imag) { - mlir::Type complexTy{fir::CplxType::get(builder.getContext(), kind)}; - mlir::Value *und{builder.create(loc, complexTy)}; - return setImagPart(setRealPart(und, real), imag); + /// Complex part access helper dynamic versions + mlir::Value *extractComplexPart(mlir::Value *cplx, bool isImagPart) { + return isImagPart ? extract(cplx) : extract(cplx); + } + mlir::Value *insertComplexPart( + mlir::Value *cplx, mlir::Value *part, bool isImagPart) { + return isImagPart ? insert(cplx, part) + : insert(cplx, part); } + // Complex operation helpers mlir::Value *createComplexCompare( mlir::Value *cplx1, mlir::Value *cplx2, bool eq) { - mlir::Value *real1{createComplexRealPart(cplx1)}; - mlir::Value *real2{createComplexRealPart(cplx2)}; - mlir::Value *imag1{createComplexImagPart(cplx1)}; - mlir::Value *imag2{createComplexImagPart(cplx2)}; + mlir::Value *real1{extract(cplx1)}; + mlir::Value *real2{extract(cplx2)}; + mlir::Value *imag1{extract(cplx1)}; + mlir::Value *imag2{extract(cplx2)}; mlir::CmpFPredicate predicate{ eq ? mlir::CmpFPredicate::UEQ : mlir::CmpFPredicate::UNE}; @@ -95,9 +95,10 @@ class ComplexHandler { } private: - inline mlir::Value *getPartId(bool isImaginaryPart) { + // Make mlir ConstantOp from template part id. + template inline mlir::Value *getPartId() { auto type{mlir::IntegerType::get(32, builder.getContext())}; - auto attr{builder.getIntegerAttr(type, isImaginaryPart ? 1 : 0)}; + auto attr{builder.getIntegerAttr(type, static_cast(partId))}; return builder.create(loc, type, attr).getResult(); } diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index d5528844ed1d..62a47c794f3e 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -14,11 +14,11 @@ #include "convert-expr.h" #include "builder.h" -#include "complex.h" +#include "complex-handler.h" #include "fe-helper.h" -#include "fir/Dialect.h" +#include "fir/FIRDialect.h" #include "fir/FIROps.h" -#include "fir/Type.h" +#include "fir/FIRType.h" #include "intrinsics.h" #include "runtime.h" #include "../common/default-kinds.h" @@ -80,12 +80,12 @@ class ExprLowering { /// unordered, but we may want to cons ordered in certain situation. static M::CmpIPredicate translateRelational(Co::RelationalOperator rop) { switch (rop) { - case Co::RelationalOperator::LT: return M::CmpIPredicate::SLT; - case Co::RelationalOperator::LE: return M::CmpIPredicate::SLE; - case Co::RelationalOperator::EQ: return M::CmpIPredicate::EQ; - case Co::RelationalOperator::NE: return M::CmpIPredicate::NE; - case Co::RelationalOperator::GT: return M::CmpIPredicate::SGT; - case Co::RelationalOperator::GE: return M::CmpIPredicate::SGE; + case Co::RelationalOperator::LT: return M::CmpIPredicate::slt; + case Co::RelationalOperator::LE: return M::CmpIPredicate::sle; + case Co::RelationalOperator::EQ: return M::CmpIPredicate::eq; + case Co::RelationalOperator::NE: return M::CmpIPredicate::ne; + case Co::RelationalOperator::GT: return M::CmpIPredicate::sgt; + case Co::RelationalOperator::GE: return M::CmpIPredicate::sge; } assert(false && "unhandled INTEGER relational operator"); return {}; @@ -93,14 +93,15 @@ class ExprLowering { /// Convert parser's REAL relational operators to MLIR. TODO: using /// unordered, but we may want to cons ordered in certain situation. - static M::CmpFPredicate translateFloatRelational(Co::RelationalOperator rop) { + static fir::CmpFPredicate translateFloatRelational( + Co::RelationalOperator rop) { switch (rop) { - case Co::RelationalOperator::LT: return M::CmpFPredicate::ULT; - case Co::RelationalOperator::LE: return M::CmpFPredicate::ULE; - case Co::RelationalOperator::EQ: return M::CmpFPredicate::UEQ; - case Co::RelationalOperator::NE: return M::CmpFPredicate::UNE; - case Co::RelationalOperator::GT: return M::CmpFPredicate::UGT; - case Co::RelationalOperator::GE: return M::CmpFPredicate::UGE; + case Co::RelationalOperator::LT: return fir::CmpFPredicate::ULT; + case Co::RelationalOperator::LE: return fir::CmpFPredicate::ULE; + case Co::RelationalOperator::EQ: return fir::CmpFPredicate::UEQ; + case Co::RelationalOperator::NE: return fir::CmpFPredicate::UNE; + case Co::RelationalOperator::GT: return fir::CmpFPredicate::UGT; + case Co::RelationalOperator::GE: return fir::CmpFPredicate::UGE; } assert(false && "unhandled REAL relational operator"); return {}; @@ -149,6 +150,19 @@ class ExprLowering { template M::Value *createBinaryOp(A const &ex) { return createBinaryOp(ex, genval(ex.left()), genval(ex.right())); } + template M::Value *createLogicalOp(A const &ex) { + auto mlirTy{M::IntegerType::get(1, builder.getContext())}; + auto *lhs{genval(ex.left())}; + auto *rhs{genval(ex.right())}; + // mlir logical ops do not work with fir.logical, so the operation + // is wrapped in conversions + auto lhsConv{builder.create(getLoc(), mlirTy, lhs)}; + auto rhsConv{builder.create(getLoc(), mlirTy, rhs)}; + auto op{createBinaryOp(ex, lhsConv, rhsConv)}; + assert(lhs); + auto resType{lhs->getType()}; + return builder.create(getLoc(), resType, op); + } M::FuncOp getFunction(L::StringRef name, M::FunctionType funTy) { auto module{getModule(&builder)}; @@ -212,24 +226,24 @@ class ExprLowering { } template M::Value *createFltCmpOp( - A const &ex, M::CmpFPredicate pred, M::Value *lhs, M::Value *rhs) { + A const &ex, fir::CmpFPredicate pred, M::Value *lhs, M::Value *rhs) { assert(lhs && rhs && "argument did not lower"); auto x = builder.create(getLoc(), pred, lhs, rhs); return x.getResult(); } template - M::Value *createFltCmpOp(A const &ex, M::CmpFPredicate pred) { + M::Value *createFltCmpOp(A const &ex, fir::CmpFPredicate pred) { return createFltCmpOp( ex, pred, genval(ex.left()), genval(ex.right())); } - M::Value *gen(Se::Symbol const *sym) { + M::Value *gen(Se::SymbolRef sym) { // FIXME: not all symbols are local return createTemporary(getLoc(), builder, symMap, - translateSymbolToFIRType(builder.getContext(), defaults, sym), sym); + translateSymbolToFIRType(builder.getContext(), defaults, sym), &*sym); } - M::Value *gendef(Se::Symbol const *sym) { return gen(sym); } - M::Value *genval(Se::Symbol const *sym) { + M::Value *gendef(Se::SymbolRef sym) { return gen(sym); } + M::Value *genval(Se::SymbolRef sym) { // Do not load the same symbols several time in one expression. // Fortran guarantees variable value must be the same wherever it // appears in one expression. @@ -254,7 +268,7 @@ class ExprLowering { } template M::Value *genval(Ev::ComplexComponent const &part) { - return ComplexHandler{builder, getLoc()}.createComplexPart( + return ComplexHandler{builder, getLoc()}.extractComplexPart( genval(part.left()), part.isImaginaryPart); } @@ -348,21 +362,25 @@ class ExprLowering { template M::Value *genval(Ev::Relational> const &op) { + mlir::Value *result{nullptr}; if constexpr (TC == IntegerCat) { - return createCompareOp(op, translateRelational(op.opr)); + result = createCompareOp(op, translateRelational(op.opr)); } else if constexpr (TC == RealCat) { - return createFltCmpOp(op, translateFloatRelational(op.opr)); + result = + createFltCmpOp(op, translateFloatRelational(op.opr)); } else if constexpr (TC == ComplexCat) { bool eq{op.opr == Co::RelationalOperator::EQ}; assert(eq || op.opr == Co::RelationalOperator::NE && "relation undefined for complex"); - return ComplexHandler{builder, getLoc()}.createComplexCompare( + result = ComplexHandler{builder, getLoc()}.createComplexCompare( genval(op.left()), genval(op.right()), eq); } else { static_assert(TC == CharacterCat); TODO(); } + auto logicalTy{getFIRType(builder.getContext(), defaults, LogicalCat)}; + return builder.create(getLoc(), logicalTy, result); } // TODO JP: the thing below should not be required. @@ -382,16 +400,24 @@ class ExprLowering { } template M::Value *genval(Ev::LogicalOperation const &op) { + mlir::Value *result{nullptr}; switch (op.logicalOperator) { - case Ev::LogicalOperator::And: return createBinaryOp(op); - case Ev::LogicalOperator::Or: return createBinaryOp(op); + case Ev::LogicalOperator::And: + result = createLogicalOp(op); + break; + case Ev::LogicalOperator::Or: result = createLogicalOp(op); break; case Ev::LogicalOperator::Eqv: - return createCompareOp(op, M::CmpIPredicate::EQ); + result = createCompareOp(op, M::CmpIPredicate::eq); + break; case Ev::LogicalOperator::Neqv: - return createCompareOp(op, M::CmpIPredicate::NE); + result = createCompareOp(op, M::CmpIPredicate::ne); + break; } - assert(false && "unhandled logical operation"); - return {}; + if (!result) { + assert(false && "unhandled logical operation"); + } + auto logicalTy{getFIRType(builder.getContext(), defaults, LogicalCat)}; + return builder.create(getLoc(), logicalTy, result); } template @@ -515,13 +541,15 @@ class ExprLowering { L::SmallVector coorArgs; auto obj{gen(*base)}; const Se::Symbol *sym{nullptr}; + M::Type ty{translateSymbolToFIRType(builder.getContext(), defaults, *sym)}; for (auto *field : list) { sym = &field->GetLastSymbol(); auto name{sym->name().ToString()}; - coorArgs.push_back(builder.create(getLoc(), name)); + // FIXME: as we're walking the chain of field names, we need to update the + // subtype as we drill down + coorArgs.push_back(builder.create(getLoc(), name, ty)); } assert(sym && "no component(s)?"); - M::Type ty{translateSymbolToFIRType(builder.getContext(), defaults, sym)}; ty = fir::ReferenceType::get(ty); return builder.create(getLoc(), ty, obj, coorArgs); } @@ -565,7 +593,7 @@ class ExprLowering { M::Value *gen(Ev::ArrayRef const &aref) { M::Value *base; if (aref.base().IsSymbol()) - base = gen(const_cast(&aref.base().GetFirstSymbol())); + base = gen(aref.base().GetFirstSymbol()); else base = gen(aref.base().GetComponent()); llvm::SmallVector args; @@ -640,7 +668,7 @@ class ExprLowering { const auto *expr{arg->UnwrapExpr()}; assert(expr && "assumed type argument requires explicit interface"); if (const Se::Symbol * sym{Ev::UnwrapWholeSymbolDataRef(*expr)}) { - M::Value *argRef{symMap.lookupSymbol(sym)}; + M::Value *argRef{symMap.lookupSymbol(*sym)}; assert(argRef && "could not get symbol reference"); argTypes.push_back(argRef->getType()); operands.push_back(argRef); @@ -709,7 +737,7 @@ M::Value *Br::createSomeAddress(M::Location loc, M::OpBuilder &builder, M::Value *Br::createTemporary(M::Location loc, M::OpBuilder &builder, SymMap &symMap, M::Type type, Se::Symbol const *symbol) { if (symbol) - if (auto *val{symMap.lookupSymbol(symbol)}) { + if (auto *val{symMap.lookupSymbol(*symbol)}) { if (auto *op{val->getDefiningOp()}) { return op->getResult(0); } @@ -721,7 +749,7 @@ M::Value *Br::createTemporary(M::Location loc, M::OpBuilder &builder, assert(!type.dyn_cast() && "cannot be a reference"); if (symbol) { ae = builder.create(loc, type, symbol->name().ToString()); - symMap.addSymbol(symbol, ae); + symMap.addSymbol(*symbol, ae); } else { ae = builder.create(loc, type); } diff --git a/lib/burnside/fe-helper.cc b/lib/burnside/fe-helper.cc index 72265d6b66fe..eaa649afed19 100644 --- a/lib/burnside/fe-helper.cc +++ b/lib/burnside/fe-helper.cc @@ -14,7 +14,7 @@ #include "fe-helper.h" #include "bridge.h" -#include "fir/Type.h" +#include "fir/FIRType.h" #include "../semantics/expression.h" #include "../semantics/tools.h" #include "../semantics/type.h" @@ -46,7 +46,9 @@ template int64_t toConstant(const Ev::Expr
&e) { } #undef TODO -#define TODO() assert(false); return {} +#define TODO() \ + assert(false && "not yet implemented"); \ + return {} // one argument template, must be specialized template @@ -218,7 +220,7 @@ class TypeBuilder { M::Type mkVoid() { return M::TupleType::get(context); } - fir::SequenceType::Shape genSeqShape(const Se::Symbol *symbol) { + fir::SequenceType::Shape genSeqShape(Se::SymbolRef symbol) { assert(symbol->IsObjectArray()); fir::SequenceType::Bounds bounds; auto &details = symbol->get(); @@ -248,17 +250,20 @@ class TypeBuilder { /// Type consing from a symbol. A symbol's type must be created from the type /// discovered by the front-end at runtime. - M::Type gen(const Se::Symbol *symbol) { + M::Type gen(Se::SymbolRef symbol) { if (auto *proc = symbol->detailsIf()) { M::Type returnTy{mkVoid()}; if (proc->isFunction()) { - returnTy = gen(&proc->result()); + returnTy = gen(proc->result()); } // FIXME: handle alt-return llvm::SmallVector inputTys; for (auto *arg : proc->dummyArgs()) { // FIXME: not all args are pass by ref - inputTys.emplace_back(fir::ReferenceType::get(gen(arg))); + // Nullptr args are alternate returns indicators + if (arg) { + inputTys.emplace_back(fir::ReferenceType::get(gen(*arg))); + } } return M::FunctionType::get(inputTys, returnTy, context); } @@ -283,6 +288,7 @@ class TypeBuilder { case DerivedCat: TODO(); break; } } else if (auto *tySpec{type->AsDerived()}) { + (void)tySpec; // FIXME TODO(); } else { assert(false && "type spec not found"); @@ -375,7 +381,7 @@ M::Type Br::translateSomeExprToFIRType(M::MLIRContext *context, // This entry point avoids gratuitously wrapping the Symbol instance in layers // of Expr that will then be immediately peeled back off and discarded. M::Type Br::translateSymbolToFIRType(M::MLIRContext *context, - Co::IntrinsicTypeDefaultKinds const &defaults, const Se::Symbol *symbol) { + Co::IntrinsicTypeDefaultKinds const &defaults, Se::SymbolRef symbol) { return TypeBuilder{context, defaults}.gen(symbol); } diff --git a/lib/burnside/fe-helper.h b/lib/burnside/fe-helper.h index 3eb90d83d9c2..bc7352339212 100644 --- a/lib/burnside/fe-helper.h +++ b/lib/burnside/fe-helper.h @@ -32,6 +32,7 @@ class Type; namespace Fortran { namespace common { class IntrinsicTypeDefaultKinds; +template class Reference; } // common namespace evaluate { @@ -50,6 +51,7 @@ class CookedSource; namespace semantics { class Symbol; +using SymbolRef = common::Reference; } // semantics namespace burnside { @@ -100,7 +102,7 @@ mlir::Type translateSomeExprToFIRType(mlir::MLIRContext *ctxt, mlir::Type translateSymbolToFIRType(mlir::MLIRContext *ctxt, common::IntrinsicTypeDefaultKinds const &defaults, - const semantics::Symbol *symbol); + const semantics::SymbolRef symbol); mlir::Type convertReal(mlir::MLIRContext *context, int KIND); diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index 4e514e6ee5d1..a80471fa4a12 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -14,12 +14,13 @@ #include "intrinsics.h" #include "builder.h" -#include "complex.h" +#include "complex-handler.h" #include "fe-helper.h" #include "fir/FIROps.h" -#include "fir/Type.h" +#include "fir/FIRType.h" #include "runtime.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/Twine.h" #include #include #include @@ -70,13 +71,16 @@ class MathRuntimeLibrary { Map library; }; +/// enum used to templatized code gen for intrinsics that are alike. +enum class Extremum { Min, Max }; + /// The implementation of IntrinsicLibrary is based on a map that associates /// Fortran intrinsics generic names to the related FIR generator functions. /// All generator functions are member functions of the Implementation class /// and they all take the same context argument that contains the name and /// arguments of the Fortran intrinsics call to lower among other things. /// A same FIR generator function may be able to generate the FIR for several -/// intrinsics. For instance generateRuntimeCall tries to find a runtime +/// intrinsics. For instance genRuntimeCall tries to find a runtime /// functions that matches the Fortran intrinsic call and generate the /// operations to call this functions if it was found. /// IntrinsicLibrary holds a constant MathRuntimeLibrary that it uses to @@ -102,6 +106,7 @@ class IntrinsicLibrary::Implementation { llvm::StringRef name; llvm::ArrayRef arguments; mlir::FunctionType funcType; + mlir::ModuleOp getModuleOp() { return getModule(builder); } mlir::MLIRContext *getMLIRContext() { return getModule(builder).getContext(); } @@ -113,7 +118,7 @@ class IntrinsicLibrary::Implementation { /// Define the different FIR generators that can be mapped to intrinsic to /// generate the related code. - using Generator = mlir::Value *(Implementation::*)(Context &)const; + using Generator = mlir::Value *(Implementation::*)(Context &) const; /// Search a runtime function that is associated to the generic intrinsic name /// and whose signature matches the intrinsic arguments and result types. /// If no such runtime function is found but a runtime function associated @@ -121,20 +126,21 @@ class IntrinsicLibrary::Implementation { /// conversions will be inserted before and/or after the call. This is to /// mainly to allow 16 bits float support even-though little or no math /// runtime is currently available for it. - mlir::Value *generateRuntimeCall(Context &) const; - /// All generators can be combined with generateWrapperCall that will build a + mlir::Value *genRuntimeCall(Context &) const; + /// All generators can be combined with genWrapperCall that will build a /// function named "fir."+ + "." + and /// generate the intrinsic implementation inside instead of at the intrinsic /// call sites. This can be used to keep the FIR more readable. - template mlir::Value *generateWrapperCall(Context &c) const { + template mlir::Value *genWrapperCall(Context &c) const { return outlineInWrapper(g, c); } /// The defaultGenerator is always attempted if no mapping was found for the /// generic name provided. mlir::Value *defaultGenerator(Context &c) const { - return generateWrapperCall<&I::generateRuntimeCall>(c); + return genWrapperCall<&I::genRuntimeCall>(c); } - mlir::Value *generateConjg(Context &) const; + mlir::Value *genConjg(Context &) const; + template mlir::Value *genExtremum(Context &) const; struct IntrinsicHanlder { const char *name; @@ -146,18 +152,23 @@ class IntrinsicLibrary::Implementation { /// defined here for a generic intrinsic, the defaultGenerator will /// be attempted. static constexpr IntrinsicHanlder handlers[]{ - {"conjg", &I::generateConjg}, + {"conjg", &I::genConjg}, + {"max", &I::genExtremum}, + {"min", &I::genExtremum}, }; // helpers - static std::string getWrapperName(Context &); mlir::Value *outlineInWrapper(Generator, Context &) const; - static mlir::FunctionType getFunctionType(mlir::Type resultType, - llvm::ArrayRef arguments, mlir::OpBuilder &); MathRuntimeLibrary runtime; }; +// helpers +static std::string getIntrinsicWrapperName( + const llvm::StringRef &intrinsic, mlir::FunctionType funTy); +static mlir::FunctionType getFunctionType(mlir::Type resultType, + llvm::ArrayRef arguments, mlir::OpBuilder &); + /// Define a simple static runtime description that will be transformed into /// RuntimeFunction when building the IntrinsicLibrary. class MathsRuntimeStaticDescription : public RuntimeStaticDescription { @@ -312,9 +323,8 @@ mlir::Value *IntrinsicLibrary::Implementation::genval(mlir::Location loc, return this->defaultGenerator(context); } -mlir::FunctionType IntrinsicLibrary::Implementation::getFunctionType( - mlir::Type resultType, llvm::ArrayRef arguments, - mlir::OpBuilder &builder) { +static mlir::FunctionType getFunctionType(mlir::Type resultType, + llvm::ArrayRef arguments, mlir::OpBuilder &builder) { llvm::SmallVector argumentTypes; for (const auto &arg : arguments) { assert(arg != nullptr); // TODO think about optionals @@ -324,23 +334,25 @@ mlir::FunctionType IntrinsicLibrary::Implementation::getFunctionType( argumentTypes, resultType, getModule(&builder).getContext()); } -std::string IntrinsicLibrary::Implementation::getWrapperName(Context &c) { - // TODO find nicer type to string infra - llvm::StringRef prefix{"fir."}; - assert(c.funcType.getNumResults() == 1); - mlir::Type resultType{c.funcType.getResult(0)}; +static std::string getIntrinsicWrapperName( + const llvm::StringRef &intrinsic, mlir::FunctionType funTy) { + // TODO find nicer type to string infra or move this in a mangling utility + auto addSuffix{[&](const llvm::Twine &suffix) -> std::string { + return ("fir." + intrinsic + suffix).str(); + }}; + assert(funTy.getNumResults() == 1 && "only function mangling supported"); + mlir::Type resultType{funTy.getResult(0)}; if (auto f{resultType.dyn_cast()}) { - return prefix.str() + c.name.str() + ".f" + std::to_string(f.getWidth()); + return addSuffix(".f" + llvm::Twine(f.getWidth())); } else if (auto i{resultType.dyn_cast()}) { - return prefix.str() + c.name.str() + ".i" + std::to_string(i.getWidth()); + return addSuffix(".i" + llvm::Twine(i.getWidth())); } else if (auto cplx{resultType.dyn_cast()}) { - // TODO using kind here is weird, but I do not want to hard coded mapping - return prefix.str() + c.name.str() + ".c" + std::to_string(cplx.getFKind()); - } else if (auto firf{resultType.dyn_cast()}) { - return prefix.str() + c.name.str() + ".f" + std::to_string(firf.getFKind()); + return addSuffix(".c" + llvm::Twine(cplx.getFKind())); + } else if (auto real{resultType.dyn_cast()}) { + return addSuffix(".r" + llvm::Twine(real.getFKind())); } else { assert(false); - return "fir." + c.name.str() + ".unknown"; + return addSuffix(".unknown"); } } @@ -348,7 +360,8 @@ mlir::Value *IntrinsicLibrary::Implementation::outlineInWrapper( Generator generator, Context &context) const { mlir::ModuleOp module{getModule(context.builder)}; mlir::MLIRContext *mlirContext{module.getContext()}; - std::string wrapperName{getWrapperName(context)}; + std::string wrapperName{ + getIntrinsicWrapperName(context.name, context.funcType)}; mlir::FuncOp function{getNamedFunction(module, wrapperName)}; if (!function) { // First time this wrapper is needed, build it. @@ -381,7 +394,7 @@ mlir::Value *IntrinsicLibrary::Implementation::outlineInWrapper( return call.getResult(0); } -mlir::Value *IntrinsicLibrary::Implementation::generateRuntimeCall( +mlir::Value *IntrinsicLibrary::Implementation::genRuntimeCall( Context &context) const { // Look up runtime mlir::FunctionType soughtFuncType{context.funcType}; @@ -421,13 +434,14 @@ mlir::Value *IntrinsicLibrary::Implementation::generateRuntimeCall( } } else { // could not find runtime function - assert(false); // TODO: better error handling. - return nullptr; + assert(false && "no runtime found for this intrinsics"); + // TODO: better error handling ? + // - Try to have compile time check of runtime compltness ? } } // CONJG -mlir::Value *IntrinsicLibrary::Implementation::generateConjg( +mlir::Value *IntrinsicLibrary::Implementation::genConjg( Context &genCtxt) const { assert(genCtxt.arguments.size() == 1); mlir::Type resType{genCtxt.getResultType()}; @@ -440,10 +454,68 @@ mlir::Value *IntrinsicLibrary::Implementation::generateConjg( mlir::Type realType{cplxHandler.getComplexPartType(cplx)}; mlir::Value *zero{builder.create( genCtxt.loc, realType, builder.getZeroAttr(realType))}; - mlir::Value *imag{cplxHandler.createComplexImagPart(cplx)}; + mlir::Value *imag{cplxHandler.extract(cplx)}; mlir::Value *negImag{ genCtxt.builder->create(genCtxt.loc, zero, imag)}; - return cplxHandler.setImagPart(cplx, negImag); + return cplxHandler.insert(cplx, negImag); +} + +static mlir::FuncOp getMergeFunc(mlir::Type type, mlir::ModuleOp module) { + llvm::SmallVector argTypes{ + type, type, fir::LogicalType::get(module.getContext(), 4)}; + auto mergeType{ + mlir::FunctionType::get(argTypes, {type}, module.getContext())}; + auto mergeName{getIntrinsicWrapperName("merge", mergeType)}; + return createFunction(module, mergeName, mergeType); +} + +template +static mlir::Value *createCompare(mlir::Location loc, mlir::OpBuilder &builder, + mlir::Value *left, mlir::Value *right) { + static constexpr auto integerPredicate{extremum == Extremum::Max + ? mlir::CmpIPredicate::sgt + : mlir::CmpIPredicate::slt}; + static constexpr auto realPredicate{extremum == Extremum::Max + ? fir::CmpFPredicate::UGT + : fir::CmpFPredicate::ULT}; + auto type{left->getType()}; + mlir::Value *result{nullptr}; + if (type.isa() || type.isa()) { + // TODO: The type we get from CmpfOp is a bit weird (it is the operands + // types, and not a boolean). + result = builder.create(loc, realPredicate, left, right); + } else if (type.isa()) { + result = builder.create(loc, integerPredicate, left, right); + } else if (type.isa()) { + // TODO: ! character min and max is tricky because the result + // length is the length of the longest argument! + // So we may need a temp. + } + assert(result); + // TODO which logical type should comparisons return ? + // This is also currently not observed in convert-expr + auto resTy{fir::LogicalType::get(getModule(&builder).getContext(), 4)}; + return builder.create(loc, resTy, result); } +// MIN and MAX +// Extremum intrinsic are lowered using the MERGE intrinsic +// to avoid introducing branches. +template +mlir::Value *IntrinsicLibrary::Implementation::genExtremum( + Context &genCtxt) const { + auto &builder{*genCtxt.builder}; + auto loc{genCtxt.loc}; + assert(genCtxt.arguments.size() >= 2); + auto *result{genCtxt.arguments[0]}; + auto mergeFunc{getMergeFunc(result->getType(), genCtxt.getModuleOp())}; + for (auto *arg : genCtxt.arguments.drop_front()) { + auto mask{createCompare(loc, builder, result, arg)}; + llvm::SmallVector mergeArgs{result, arg, mask}; + result = + builder.create(loc, mergeFunc, mergeArgs).getResult(0); + } + return result; } + +} // namespace Fortran::burnside diff --git a/lib/burnside/runtime.cc b/lib/burnside/runtime.cc index 453dbb9faf64..fcf7f9d1d722 100644 --- a/lib/burnside/runtime.cc +++ b/lib/burnside/runtime.cc @@ -14,7 +14,7 @@ #include "runtime.h" #include "builder.h" -#include "fir/Type.h" +#include "fir/FIRType.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -44,9 +44,8 @@ mlir::Type RuntimeStaticDescription::getMLIRType( mlir::FunctionType RuntimeStaticDescription::getMLIRFunctionType( mlir::MLIRContext *context) const { llvm::SmallVector argMLIRTypes; - for (const TypeCode *t{argumentTypeCodes.start}; - t != nullptr && t != argumentTypeCodes.end; ++t) { - argMLIRTypes.push_back(getMLIRType(*t, context)); + for (const TypeCode &t : argumentTypeCodes) { + argMLIRTypes.push_back(getMLIRType(t, context)); } if (resultTypeCode.has_value()) { mlir::Type resMLIRType{getMLIRType(*resultTypeCode, context)}; diff --git a/lib/burnside/runtime.h b/lib/burnside/runtime.h index 86f3c208f35b..c8d77126fb27 100644 --- a/lib/burnside/runtime.h +++ b/lib/burnside/runtime.h @@ -32,13 +32,57 @@ namespace Fortran::burnside { /// [Coding style](https://llvm.org/docs/CodingStandards.html) +/// C++ does not provide variable size constexpr container yet. +/// StaticVector is a class that can be used to hold constexpr data as if it was +/// a vector (i.e, the number of element is not reflected in the +/// container type). This is useful to use in classes that need to be constexpr +/// and where leaking the size as a template type would make it harder to +/// manipulate. It can hold whatever data that can appear as non-type templates +/// (integers, enums, pointer to objects, function pointers...). +/// Example usage: +/// +/// enum class Enum {A, B}; +/// constexpr StaticVector vec{StaticVector::create()}; +/// for (const Enum& code : vec) { /*...*/ } +/// + +/// This is the class where the constexpr data is "allocated". In fact +/// the data is stored "in" the type. Objects of this type are not meant to +/// be ever constructed. +template struct StaticVectorStorage { + static constexpr T values[]{v...}; + static constexpr const T *start{&values[0]}; + static constexpr const T *end{start + sizeof...(v)}; +}; +template struct StaticVectorStorage { + static constexpr const T *start{nullptr}, *end{nullptr}; +}; + +/// StaticVector cannot be directly constructed, instead its +/// `create` static method has to be used to create StaticVector objects. +/// StaticVector are views over the StaticVectorStorage type that was built +/// while instantiating the create method. They do not duplicate the values from +/// these read-only storages. +template struct StaticVector { + template static constexpr StaticVector create() { + using storage = StaticVectorStorage; + return StaticVector{storage::start, storage::end}; + } + using const_iterator = const T *; + constexpr const_iterator begin() const { return startPtr; } + constexpr const_iterator end() const { return endPtr; } + const T *startPtr{nullptr}; + const T *endPtr{nullptr}; +}; + /// Define a simple static runtime description that different runtime can /// derived from (e.g io, maths ...). /// This base class only define enough to generate the functuion declarations, -// it is up to the actual runtime descriptions to define a way to organize these -// descriptions in a meaningful way. +/// it is up to the actual runtime descriptions to define a way to organize +/// these descriptions in a meaningful way. /// It is constexpr constructible so that static tables of such descriptions can /// be safely stored as global variables without requiring global constructors. + class RuntimeStaticDescription { public: /// Define possible runtime function argument/return type used in signature @@ -46,22 +90,9 @@ class RuntimeStaticDescription { /// directly be used because they can only be dynamically built. enum TypeCode { i32, i64, f32, f64, c32, c64, IOCookie }; using MaybeTypeCode = std::optional; // for results + using TypeCodeVector = StaticVector; // for arguments static constexpr MaybeTypeCode voidTy{MaybeTypeCode{std::nullopt}}; - /// C++ does not provide variable size constexpr container yet. TypeVector - /// implements one for Type elements. It works because Type is an enumeration. - struct TypeCodeVector { - template struct Storage { - static constexpr TypeCode values[]{v...}; - }; - template static constexpr TypeCodeVector create() { - const TypeCode *start{&Storage::values[0]}; - return TypeCodeVector{start, start + sizeof...(v)}; - } - template<> constexpr TypeCodeVector create<>() { return TypeCodeVector{}; } - const TypeCode *start{nullptr}; - const TypeCode *end{nullptr}; - }; constexpr RuntimeStaticDescription( const char *s, MaybeTypeCode r, TypeCodeVector a) : symbol{s}, resultTypeCode{r}, argumentTypeCodes{a} {} @@ -88,7 +119,7 @@ class RuntimeStaticDescription { // TODO: Find a better place for this if this is retained. // This is currently here because this was designed to provide // maps over runtime description without the burden of having to -// instantiate these maps dynamically and to keep they somewhere. +// instantiate these maps dynamically and to keep them somewhere. template class StaticMultimapView { public: using Key = typename Value::Key; @@ -114,7 +145,7 @@ template class StaticMultimapView { // TODO: sorted // non empty increasing pointer direction return !range.empty(); - }; + } constexpr const_iterator begin() const { return range.begin(); } constexpr const_iterator end() const { return range.end(); } diff --git a/lib/fir/Attribute.cpp b/lib/fir/Attribute.cpp index 7c7bdd05f46c..62338be35e3c 100644 --- a/lib/fir/Attribute.cpp +++ b/lib/fir/Attribute.cpp @@ -13,10 +13,11 @@ // limitations under the License. #include "fir/Attribute.h" -#include "fir/Dialect.h" -#include "fir/Type.h" +#include "fir/FIRDialect.h" +#include "fir/FIRType.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/StringRef.h" @@ -27,101 +28,6 @@ namespace M = mlir; using namespace fir; -namespace { - -// Our very stripped down parser for Attributes. -// TODO: clean this up -class AttributeParser { -public: - AttributeParser(L::StringRef rawText) - : srcBuff{rawText}, srcPtr{rawText.begin()} {} - - M::Attribute parseAttribute(FIROpsDialect *dialect, M::Location loc) { - skipWhitespace(); - L::StringRef keyword{lexKeyword()}; - skipWhitespace(); - auto data{srcBuff.drop_front(srcPtr - srcBuff.begin())}; - if (keyword == ExactTypeAttr::getAttrName()) { - data = consumeAngles(loc, data); - M::Type type{M::parseType(data, dialect->getContext())}; - return ExactTypeAttr::get(type); - } - if (keyword == SubclassAttr::getAttrName()) { - data = consumeAngles(loc, data); - M::Type type{M::parseType(data, dialect->getContext())}; - return SubclassAttr::get(type); - } - if (keyword == PointIntervalAttr::getAttrName()) { - return PointIntervalAttr::get(dialect->getContext()); - } - if (keyword == LowerBoundAttr::getAttrName()) { - return LowerBoundAttr::get(dialect->getContext()); - } - if (keyword == UpperBoundAttr::getAttrName()) { - return UpperBoundAttr::get(dialect->getContext()); - } - if (keyword == ClosedIntervalAttr::getAttrName()) { - return ClosedIntervalAttr::get(dialect->getContext()); - } - L::Twine msg{"unknown FIR attribute: "}; - M::emitError(loc, msg.concat(srcBuff)); - return {}; - } - -private: - bool atEnd() const { return srcPtr == srcBuff.end(); } - - L::StringRef lexKeyword() { - const char *tokStart = srcPtr; - while (!atEnd() && (std::isalnum(*srcPtr) || *srcPtr == '_')) { - ++srcPtr; - } - return {tokStart, static_cast(srcPtr - tokStart)}; - } - - void skipWhitespace() { - while (!atEnd()) { - switch (*srcPtr) { - case ' ': - case '\f': - case '\n': - case '\r': - case '\t': - case '\v': - ++srcPtr; - continue; - default: - break; - } - break; - } - } - - L::StringRef consumeAngles(M::Location loc, L::StringRef data) { - data = data.ltrim().rtrim(); - if (data.size() <= 2) { - M::emitError(loc, "expecting '<' type '>' in attribute"); - return {}; - } - if (data.front() != '<') { - M::emitError(loc, "expecting '<' in attribute"); - return {}; - } - data = data.drop_front(1); - if (data.back() != '>') { - M::emitError(loc, "expecting '>' in attribute"); - return {}; - } - data = data.drop_back(1); - return data; - } - - L::StringRef srcBuff; - const char *srcPtr; -}; - -} // namespace - namespace fir { namespace detail { @@ -162,20 +68,82 @@ 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()); } -M::Attribute parseFirAttribute(FIROpsDialect *dialect, L::StringRef rawText, - M::Type type, M::Location loc) { - AttributeParser parser{rawText}; - return parser.parseAttribute(dialect, loc); +M::Attribute parseFirAttribute(FIROpsDialect *dialect, + M::DialectAsmParser &parser, M::Type type) { + auto loc = parser.getNameLoc(); + L::StringRef attrName; + if (parser.parseKeyword(&attrName)) { + parser.emitError(loc, "expected an attribute name"); + return {}; + } + + if (attrName == ExactTypeAttr::getAttrName()) { + M::Type type; + if (parser.parseLess() || parser.parseType(type) || parser.parseGreater()) { + parser.emitError(loc, "expected a type"); + } + return ExactTypeAttr::get(type); + } + if (attrName == SubclassAttr::getAttrName()) { + M::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()); + } + + L::Twine msg{"unknown FIR attribute: "}; + parser.emitError(loc, msg.concat(attrName)); + return {}; +} + +void printFirAttribute(FIROpsDialect *dialect, M::Attribute attr, + M::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 { + assert(false && "attribute pretty-printer is not implemented"); + } } } // namespace fir diff --git a/lib/fir/CMakeLists.txt b/lib/fir/CMakeLists.txt index f10d851171b8..54587daaf7f1 100644 --- a/lib/fir/CMakeLists.txt +++ b/lib/fir/CMakeLists.txt @@ -16,13 +16,14 @@ add_subdirectory(Transforms) add_llvm_library(FIR Attribute.cpp - Dialect.cpp + FIRDialect.cpp FIROps.cpp + FIRType.cpp + InternalNames.cpp IteratedDominanceFrontier.cpp KindMapping.cpp StdConverter.cpp Tilikum.cpp - Type.cpp ) add_dependencies(FIR FIROpsIncGen) diff --git a/lib/fir/Dialect.cpp b/lib/fir/FIRDialect.cpp similarity index 54% rename from lib/fir/Dialect.cpp rename to lib/fir/FIRDialect.cpp index 432686041ced..a8b869202ba2 100644 --- a/lib/fir/Dialect.cpp +++ b/lib/fir/FIRDialect.cpp @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "fir/Dialect.h" +#include "fir/FIRDialect.h" #include "fir/Attribute.h" #include "fir/FIROps.h" -#include "fir/Type.h" +#include "fir/FIRType.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/IR/StandardTypes.h" #include "mlir/Transforms/SideEffectsInterface.h" @@ -43,13 +43,16 @@ void selectBuild(M::OpBuilder *builder, M::OperationState *result, result->addSuccessor(block, blkArgs); } } + +M::DialectRegistration FIROps; + } // namespace fir::FIROpsDialect::FIROpsDialect(M::MLIRContext *ctx) : M::Dialect("fir", ctx) { addTypes(); + FieldType, HeapType, IntType, LenType, LogicalType, PointerType, + RealType, RecordType, ReferenceType, SequenceType, TypeDescType>(); addAttributes(); addOperations(this), rawData, loc); +M::Type fir::FIROpsDialect::parseType(M::DialectAsmParser &parser) const { + return parseFirType(const_cast(this), parser); } -void fir::FIROpsDialect::printType(M::Type ty, llvm::raw_ostream &os) const { - return printFirType(const_cast(this), ty, os); +void fir::FIROpsDialect::printType(M::Type ty, M::DialectAsmPrinter &p) const { + return printFirType(const_cast(this), ty, p); } -M::Attribute fir::FIROpsDialect::parseAttribute(llvm::StringRef rawText, - M::Type type, - M::Location loc) const { - return parseFirAttribute(const_cast(this), rawText, type, - loc); +M::Attribute fir::FIROpsDialect::parseAttribute(M::DialectAsmParser &parser, + M::Type type) const { + return parseFirAttribute(const_cast(this), parser, type); } void fir::FIROpsDialect::printAttribute(M::Attribute attr, - llvm::raw_ostream &os) const { - if (auto exact = attr.dyn_cast()) { - os << fir::ExactTypeAttr::getAttrName() << '<' << exact.getType() << '>'; - } else if (auto sub = attr.dyn_cast()) { - os << fir::SubclassAttr::getAttrName() << '<' << sub.getType() << '>'; - } 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 { - assert(false && "attribute pretty-printer is not implemented"); - } + M::DialectAsmPrinter &p) const { + printFirAttribute(const_cast(this), attr, p); } diff --git a/lib/fir/FIROps.cpp b/lib/fir/FIROps.cpp index 25a99b0656c0..bdff4e57f8df 100644 --- a/lib/fir/FIROps.cpp +++ b/lib/fir/FIROps.cpp @@ -14,10 +14,12 @@ #include "fir/FIROps.h" #include "fir/Attribute.h" -#include "fir/Type.h" +#include "fir/FIRType.h" #include "mlir/Dialect/StandardOps/Ops.h" +#include "mlir/IR/Diagnostics.h" #include "mlir/IR/StandardTypes.h" #include "mlir/IR/SymbolTable.h" +#include "llvm/ADT/StringSwitch.h" namespace L = llvm; namespace M = mlir; @@ -42,6 +44,16 @@ M::Type AllocMemOp::getAllocatedType() { M::Type AllocMemOp::getRefTy(M::Type ty) { return HeapType::get(ty); } +// BoxDimsOp + +/// Get the result types packed in a tuple tuple +M::Type BoxDimsOp::getTupleType() { + L::SmallVector triple{getResult(0)->getType(), + getResult(1)->getType(), + getResult(2)->getType()}; + return mlir::TupleType::get(triple, getContext()); +} + // CallOp void printCallOp(M::OpAsmPrinter &p, fir::CallOp &op) { @@ -49,7 +61,7 @@ void printCallOp(M::OpAsmPrinter &p, fir::CallOp &op) { bool isDirect = callee.hasValue(); p << op.getOperationName() << ' '; if (isDirect) - p.printSymbolName(callee.getValue()); + p << callee.getValue(); else p << *op.getOperand(0); p << '('; @@ -77,7 +89,7 @@ M::ParseResult parseCallOp(M::OpAsmParser &parser, M::OperationState &result) { Type type; if (parser.parseOperandList(operands, M::OpAsmParser::Delimiter::Paren) || - parser.parseOptionalAttributeDict(attrs) || parser.parseColon() || + parser.parseOptionalAttrDict(attrs) || parser.parseColon() || parser.parseType(type)) return M::failure(); @@ -104,6 +116,137 @@ M::ParseResult parseCallOp(M::OpAsmParser &parser, M::OperationState &result) { return M::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 +M::ParseResult parseCmpOp(M::OpAsmParser &parser, M::OperationState &result) { + L::SmallVector ops; + L::SmallVector attrs; + M::Attribute predicateNameAttr; + M::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. + L::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(); + M::Type i1Type = builder.getI1Type(); + attrs[0].second = builder.getI64IntegerAttr(static_cast(predicate)); + result.attributes = attrs; + result.addTypes({i1Type}); + return success(); +} + +M::ParseResult parseCmpfOp(M::OpAsmParser &parser, M::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); } + +M::ParseResult parseCmpcOp(M::OpAsmParser &parser, M::OperationState &result) { + return parseCmpOp(parser, result); +} + // DispatchOp M::FunctionType DispatchOp::getFunctionType() { @@ -117,9 +260,8 @@ void DispatchTableOp::build(M::Builder *builder, M::OperationState *result, L::StringRef name, M::Type type, L::ArrayRef attrs) { result->addAttribute("method", builder->getStringAttr(name)); - for (const auto &pair : attrs) { + for (const auto &pair : attrs) result->addAttribute(pair.first, pair.second); - } } M::ParseResult DispatchTableOp::parse(M::OpAsmParser &parser, @@ -131,7 +273,7 @@ M::ParseResult DispatchTableOp::parse(M::OpAsmParser &parser, // Convert the parsed name attr into a string attr. result.attributes.back().second = - parser.getBuilder().getStringAttr(nameAttr.getValue()); + parser.getBuilder().getStringAttr(nameAttr.getRootReference()); // Parse the optional table body. M::Region *body = result.addRegion(); @@ -173,9 +315,8 @@ void GlobalOp::build(M::Builder *builder, M::OperationState *result, result->addAttribute(getTypeAttrName(), M::TypeAttr::get(type)); result->addAttribute(M::SymbolTable::getSymbolAttrName(), builder->getStringAttr(name)); - for (const auto &pair : attrs) { + for (const auto &pair : attrs) result->addAttribute(pair.first, pair.second); - } } M::ParseResult GlobalOp::parse(M::OpAsmParser &parser, @@ -187,19 +328,18 @@ M::ParseResult GlobalOp::parse(M::OpAsmParser &parser, return failure(); auto &builder = parser.getBuilder(); - result.attributes.back().second = builder.getStringAttr(nameAttr.getValue()); + auto name = nameAttr.getRootReference(); + result.attributes.back().second = builder.getStringAttr(name); - if (parser.parseOptionalKeyword("constant")) { - result.addAttribute("constant", builder.getBoolAttr(false)); - } else { + if (!parser.parseOptionalKeyword("constant")) { // if "constant" keyword then mark this as a constant, not a variable - result.addAttribute("constant", builder.getBoolAttr(true)); + result.addAttribute("constant", builder.getUnitAttr()); } M::Type globalType; - if (parser.parseColonType(globalType)) { + if (parser.parseColonType(globalType)) return M::failure(); - } + result.addAttribute(getTypeAttrName(), M::TypeAttr::get(globalType)); // Parse the optional initializer body. @@ -217,7 +357,7 @@ void GlobalOp::print(M::OpAsmPrinter &p) { auto varName = getAttrOfType(M::SymbolTable::getSymbolAttrName()).getValue(); p << getOperationName() << " @" << varName; - if (getAttr("constant").cast().getValue()) + if (getAttr("constant")) p << " constant"; p << " : "; p.printType(getType()); @@ -283,19 +423,16 @@ M::ParseResult parseLoopOp(M::OpAsmParser &parser, M::OperationState &result) { parser.parseKeyword("to") || parser.parseOperand(ub) || parser.resolveOperand(ub, indexType, result.operands)) return M::failure(); - if (parser.parseOptionalKeyword(LoopOp::getStepKeyword())) { + + if (parser.parseOptionalKeyword(LoopOp::getStepKeyword())) result.addAttribute(LoopOp::getStepKeyword(), builder.getIntegerAttr(builder.getIndexType(), 1)); - } else if (parser.parseOperand(step) || - parser.resolveOperand(step, indexType, result.operands)) { + else if (parser.parseOperand(step) || + parser.resolveOperand(step, indexType, result.operands)) return M::failure(); - } - if (parser.parseOptionalKeyword("unordered")) { - // ok - } else { - result.addAttribute("unordered", builder.getBoolAttr(true)); - } + if (!parser.parseOptionalKeyword("unordered")) + result.addAttribute("unordered", builder.getUnitAttr()); // Parse the body region. M::Region *body = result.addRegion(); @@ -305,17 +442,15 @@ M::ParseResult parseLoopOp(M::OpAsmParser &parser, M::OperationState &result) { fir::LoopOp::ensureTerminator(*body, builder, result.location); // Parse the optional attribute list. - if (parser.parseOptionalAttributeDict(result.attributes)) { + if (parser.parseOptionalAttrDict(result.attributes)) return M::failure(); - } return M::success(); } fir::LoopOp getForInductionVarOwner(M::Value *val) { auto *ivArg = dyn_cast(val); - if (!ivArg) { + if (!ivArg) return fir::LoopOp(); - } assert(ivArg->getOwner() && "unlinked block argument"); auto *containingInst = ivArg->getOwner()->getParentOp(); return dyn_cast_or_null(containingInst); @@ -358,23 +493,21 @@ M::ParseResult parseWhereOp(M::OpAsmParser &parser, M::OperationState &result) { parser.resolveOperand(cond, i1Type, result.operands)) return M::failure(); - if (parser.parseRegion(*thenRegion, {}, {})) { + if (parser.parseRegion(*thenRegion, {}, {})) return M::failure(); - } + WhereOp::ensureTerminator(*thenRegion, parser.getBuilder(), result.location); if (!parser.parseOptionalKeyword("otherwise")) { - if (parser.parseRegion(*elseRegion, {}, {})) { + if (parser.parseRegion(*elseRegion, {}, {})) return M::failure(); - } WhereOp::ensureTerminator(*elseRegion, parser.getBuilder(), result.location); } // Parse the optional attribute list. - if (parser.parseOptionalAttributeDict(result.attributes)) { + if (parser.parseOptionalAttrDict(result.attributes)) return M::failure(); - } return M::success(); } @@ -395,9 +528,8 @@ unsigned getCaseArgumentOffset(L::ArrayRef cases, unsigned dest) { auto &attr = cases[i]; if (!attr.dyn_cast_or_null()) { ++o; - if (attr.dyn_cast_or_null()) { + if (attr.dyn_cast_or_null()) ++o; - } } } return o; @@ -413,15 +545,26 @@ M::ParseResult parseSelector(M::OpAsmParser &parser, M::OperationState &result, return M::success(); } -void printComplexBinaryOp(Operation *op, OpAsmPrinter &p) { - assert(op->getNumOperands() == 2 && "binary op should have two operands"); - assert(op->getNumResults() == 1 && "binary op should have one result"); +/// 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(); +} + // Tablegen operators #define GET_OP_CLASSES diff --git a/lib/fir/Type.cpp b/lib/fir/FIRType.cpp similarity index 53% rename from lib/fir/Type.cpp rename to lib/fir/FIRType.cpp index 57b36a1620d4..8b020f6e88d7 100644 --- a/lib/fir/Type.cpp +++ b/lib/fir/FIRType.cpp @@ -12,17 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "fir/Type.h" -#include "fir/Dialect.h" +#include "fir/FIRType.h" +#include "fir/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/StringRef.h" #include "llvm/ADT/StringSet.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Support/raw_ostream.h" namespace L = llvm; namespace M = mlir; @@ -31,581 +30,162 @@ using namespace fir; namespace { -// Tokens - -enum class TokenKind { - error, - eof, - leftang, - rightang, - leftparen, - rightparen, - leftbrace, - rightbrace, - leftbracket, - rightbracket, - colon, - comma, - period, - eroteme, - ecphoneme, - star, - arrow, - ident, - string, - intlit, -}; - -struct Token { - TokenKind kind; - L::StringRef text; -}; - -// Lexer - -class Lexer { -public: - Lexer(L::StringRef source) : srcBuff{source}, srcPtr{source.begin()} {} - - // consume and return next token from the input. input is advanced to after - // the token. - Token lexToken(); - - int nextNonWSChar() { - skipWhitespace(); - if (atEnd()) - return -1; - return *srcPtr++; - } - - // peek ahead to the next non-whitespace character, leaving it on the input - // stream - char nextChar() { - skipWhitespace(); - if (atEnd()) - return '\0'; - return *srcPtr; - } - - // advance the input stream `count` characters - void advance(unsigned count = 1) { - while (count--) { - if (atEnd()) - break; - ++srcPtr; - } - } - - const char *getMarker() { return srcPtr; } - - L::StringRef getNextType() { - char const *const marker = srcPtr; - L::SmallVector nestedPunc; - for (bool scanning = true; scanning && !atEnd(); ++srcPtr) { - char c = *srcPtr; - switch (c) { - case '<': - case '[': - case '(': - case '{': - nestedPunc.push_back(c); - break; - case '>': - if (nestedPunc.empty() || nestedPunc.pop_back_val() != '<') { - goto done; - } - continue; - case ']': - if (nestedPunc.empty() || nestedPunc.pop_back_val() != '[') { - goto done; - } - continue; - case ')': - if (nestedPunc.empty() || nestedPunc.pop_back_val() != '(') { - goto done; - } - continue; - case '}': - if (nestedPunc.empty() || nestedPunc.pop_back_val() != '{') { - goto done; - } - continue; - case ',': - if (nestedPunc.empty()) { - goto done; - } - continue; - case '-': - if ((srcPtr + 1 != srcBuff.end()) && *(srcPtr + 1) == '>') { - ++srcPtr; - } - continue; - done: - --srcPtr; - [[fallthrough]]; - case '\0': { - scanning = false; - } break; - default: - break; - } - } - std::size_t count = srcPtr - marker; - return {marker, count}; - } - -private: - void skipWhitespace() { - while (!atEnd()) { - switch (*srcPtr) { - case ' ': - case '\f': - case '\n': - case '\r': - case '\t': - case '\v': - ++srcPtr; - continue; - default: - break; - } - break; - } - } - - Token formToken(TokenKind kind, const char *tokStart) { - return Token{kind, L::StringRef(tokStart, srcPtr - tokStart)}; - } - - Token emitError(const char *loc, const L::Twine &message) { - return formToken(TokenKind::error, loc); - } - - bool atEnd() const { return srcPtr == srcBuff.end(); } - - Token lexIdent(const char *tokStart); - Token lexNumber(const char *tokStart); - Token lexString(const char *tokStart); - - L::StringRef srcBuff; - const char *srcPtr; -}; - -Token Lexer::lexToken() { - skipWhitespace(); - if (atEnd()) { - return formToken(TokenKind::eof, ""); - } - - const char *tokStart = srcPtr; - switch (*srcPtr++) { - case '<': - return formToken(TokenKind::leftang, tokStart); - case '>': - return formToken(TokenKind::rightang, tokStart); - case '{': - return formToken(TokenKind::leftbrace, tokStart); - case '}': - return formToken(TokenKind::rightbrace, tokStart); - case '[': - return formToken(TokenKind::leftbracket, tokStart); - case ']': - return formToken(TokenKind::rightbracket, tokStart); - case '(': - return formToken(TokenKind::leftparen, tokStart); - case ')': - return formToken(TokenKind::rightparen, tokStart); - case ':': - return formToken(TokenKind::colon, tokStart); - case ',': - return formToken(TokenKind::comma, tokStart); - case '"': - return lexString(tokStart + 1); - case '-': - if (*srcPtr == '>') { - srcPtr++; - return formToken(TokenKind::arrow, tokStart); - } - return lexNumber(tokStart); - case '+': - return lexNumber(tokStart + 1); - case '!': - return formToken(TokenKind::ecphoneme, tokStart); - case '?': - return formToken(TokenKind::eroteme, tokStart); - case '*': - return formToken(TokenKind::star, tokStart); - case '.': - return formToken(TokenKind::period, tokStart); - default: - if (std::isalpha(*tokStart)) { - return lexIdent(tokStart); - } - if (std::isdigit(*tokStart)) { - return lexNumber(tokStart); - } - return emitError(tokStart, "unexpected character"); +template +TYPE parseIntSingleton(M::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); } -Token Lexer::lexString(const char *tokStart) { - while (!atEnd() && *srcPtr != '"') { - ++srcPtr; - } - Token token{formToken(TokenKind::string, tokStart)}; - ++srcPtr; - return token; +template +TYPE parseKindSingleton(M::DialectAsmParser &parser) { + return parseIntSingleton(parser); } -Token Lexer::lexIdent(const char *tokStart) { - while (!atEnd() && (std::isalnum(*srcPtr) || *srcPtr == '_')) { - ++srcPtr; - } - return formToken(TokenKind::ident, tokStart); +template +TYPE parseRankSingleton(M::DialectAsmParser &parser) { + return parseIntSingleton(parser); } -Token Lexer::lexNumber(const char *tokStart) { - while (!atEnd() && std::isdigit(*srcPtr)) { - ++srcPtr; +template +TYPE parseTypeSingleton(M::DialectAsmParser &parser, M::Location) { + M::Type ty; + if (parser.parseLess() || parser.parseType(ty) || parser.parseGreater()) { + parser.emitError(parser.getCurrentLocation(), "type expected"); + return {}; } - return formToken(TokenKind::intlit, tokStart); + return TYPE::get(ty); } -class auto_counter { -public: - explicit auto_counter(int &ref) : counter(ref) { ++counter; } - ~auto_counter() { --counter; } - -private: - auto_counter() = delete; - auto_counter(const auto_counter &) = delete; - int &counter; -}; - -/// A FIROpsDialect instance uses a FIRTypeParser object to parse and -/// instantiate all FIR types from .fir files. -class FIRTypeParser { -public: - FIRTypeParser(FIROpsDialect *dialect, L::StringRef rawData, M::Location loc) - : context{dialect->getContext()}, lexer{rawData}, loc{loc} {} - - M::Type parseType(); - -protected: - inline void emitError(M::Location loc, const L::Twine &msg) { - M::emitError(loc, msg); - } - - bool consumeToken(TokenKind tk, const L::Twine &msg) { - auto token = lexer.lexToken(); - if (token.kind != tk) { - emitError(loc, msg); - return true; // error! - } - return false; - } - - bool consumeChar(char ch, const L::Twine &msg) { - auto lexCh = lexer.nextChar(); - if (lexCh != ch) { - emitError(loc, msg); - return true; // error! - } - lexer.advance(); - return false; - } - - template - A parseIntLitSingleton(const char *msg) { - if (consumeToken(TokenKind::leftang, "expected '<' in type")) { - return {}; - } - auto token{lexer.lexToken()}; - if (token.kind != TokenKind::intlit) { - emitError(loc, msg); - return {}; - } - KindTy kind; - if (token.text.getAsInteger(0, kind)) { - emitError(loc, "expected integer constant"); - return {}; - } - if (consumeToken(TokenKind::rightang, "expected '>' in type")) { - return {}; - } - if (checkAtEnd()) - return {}; - return A::get(getContext(), kind); - } - - // `<` kind `>` - template - A parseKindSingleton() { - return parseIntLitSingleton("expected kind parameter"); - } - - // `<` rank `>` - template - A parseRankSingleton() { - return parseIntLitSingleton("expected rank parameter"); - } - - // '<' type '>' - template - A parseTypeSingleton() { - if (consumeToken(TokenKind::leftang, "expected '<' in type")) { - return {}; - } - auto ofTy = parseNextType(); - if (!ofTy) { - emitError(loc, "expected type parameter"); - return {}; - } - if (consumeToken(TokenKind::rightang, "expected '>' in type")) { - return {}; - } - if (checkAtEnd()) - return {}; - return A::get(ofTy); - } - - M::Type parseNextType(); - - bool checkAtEnd() { - if (!recursiveCall) { - auto token = lexer.lexToken(); - if (token.kind != TokenKind::eof) { - emitError(loc, "unexpected extra characters"); - return true; - } - } - return false; +// `box` `<` type (',' affine-map)? `>` +BoxType parseBox(M::DialectAsmParser &parser, M::Location loc) { + M::Type ofTy; + if (parser.parseLess() || parser.parseType(ofTy)) { + parser.emitError(parser.getCurrentLocation(), "expected type parameter"); + return {}; } - // `box` `<` type (',' affine-map)? `>` - BoxType parseBox() { - if (consumeToken(TokenKind::leftang, "expected '<' in type")) { - return {}; - } - auto ofTy = parseNextType(); - if (!ofTy) { - emitError(loc, "expected type parameter"); - return {}; - } - auto token = lexer.lexToken(); - M::AffineMapAttr map; - if (token.kind == TokenKind::comma) { - map = parseAffineMapAttr(); - token = lexer.lexToken(); - } - if (token.kind != TokenKind::rightang) { - emitError(loc, "expected '>' in type"); + M::AffineMapAttr map; + if (!parser.parseOptionalComma()) + if (parser.parseAttribute(map)) { + parser.emitError(parser.getCurrentLocation(), "expected affine map"); return {}; } - if (checkAtEnd()) - return {}; - return BoxType::get(ofTy, map); - } - - // `boxchar` `<` kind `>` - BoxCharType parseBoxChar() { return parseKindSingleton(); } - - // `boxproc` `<` return-type `>` - BoxProcType parseBoxProc() { return parseTypeSingleton(); } - - // `char` `<` kind `>` - CharacterType parseCharacter() { return parseKindSingleton(); } - - // `dims` `<` rank `>` - DimsType parseDims() { return parseRankSingleton(); } - - // `field` - FieldType parseField() { - if (checkAtEnd()) - return {}; - return FieldType::get(getContext()); + if (parser.parseGreater()) { + parser.emitError(parser.getCurrentLocation(), "expected '>'"); + return {}; } + return BoxType::get(ofTy, map); +} - // `logical` `<` kind `>` - LogicalType parseLogical() { return parseKindSingleton(); } - - // `int` `<` kind `>` - IntType parseInteger() { return parseKindSingleton(); } +// `boxchar` `<` kind `>` +BoxCharType parseBoxChar(M::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} - // `complex` `<` kind `>` - CplxType parseComplex() { return parseKindSingleton(); } +// `boxproc` `<` return-type `>` +BoxProcType parseBoxProc(M::DialectAsmParser &parser, M::Location loc) { + return parseTypeSingleton(parser, loc); +} - // `real` `<` kind `>` - RealType parseReal() { return parseKindSingleton(); } +// `char` `<` kind `>` +CharacterType parseCharacter(M::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} - // `ref` `<` type `>` - ReferenceType parseReference() { return parseTypeSingleton(); } +// `complex` `<` kind `>` +CplxType parseComplex(M::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} - // `ptr` `<` type `>` - PointerType parsePointer() { return parseTypeSingleton(); } +// `dims` `<` rank `>` +DimsType parseDims(M::DialectAsmParser &parser) { + return parseRankSingleton(parser); +} - // `heap` `<` type `>` - HeapType parseHeap() { return parseTypeSingleton(); } +// `field` +FieldType parseField(M::DialectAsmParser &parser) { + return FieldType::get(parser.getBuilder().getContext()); +} - SequenceType::Shape parseShape(); - SequenceType parseSequence(); +// `heap` `<` type `>` +HeapType parseHeap(M::DialectAsmParser &parser, M::Location loc) { + return parseTypeSingleton(parser, loc); +} - RecordType::TypeList parseTypeList(); - RecordType::TypeList parseLenParamList(); - RecordType parseDerived(); - RecordType verifyDerived(RecordType derivedTy, - llvm::ArrayRef lenPList, - llvm::ArrayRef typeList); +// `int` `<` kind `>` +IntType parseInteger(M::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} - // `tdesc` `<` type `>` - TypeDescType parseTypeDesc() { return parseTypeSingleton(); } +// `len` +LenType parseLen(M::DialectAsmParser &parser) { + return LenType::get(parser.getBuilder().getContext()); +} - // `void` - M::Type parseVoid() { - if (checkAtEnd()) - return {}; - return M::TupleType::get(getContext()); - } +// `logical` `<` kind `>` +LogicalType parseLogical(M::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} - M::MLIRContext *getContext() const { return context; } +// `ptr` `<` type `>` +PointerType parsePointer(M::DialectAsmParser &parser, M::Location loc) { + return parseTypeSingleton(parser, loc); +} - M::AffineMapAttr parseAffineMapAttr(); +// `real` `<` kind `>` +RealType parseReal(M::DialectAsmParser &parser) { + return parseKindSingleton(parser); +} -private: - M::MLIRContext *context; - Lexer lexer; - M::Location loc; - int recursiveCall{-1}; -}; +// `ref` `<` type `>` +ReferenceType parseReference(M::DialectAsmParser &parser, M::Location loc) { + return parseTypeSingleton(parser, loc); +} -// If this is a `!fir.x` type then recursively parse it now, otherwise figure -// out its extent and call into the standard type parser. -M::Type FIRTypeParser::parseNextType() { - return M::parseType(lexer.getNextType(), getContext()); -} - -// Parses either `*` `:` -// or (int | `?`) (`x` (int | `?`))* `:` -SequenceType::Shape FIRTypeParser::parseShape() { - SequenceType::Bounds bounds; - int extent; - int nextChar; - Token token = lexer.lexToken(); - if (token.kind == TokenKind::star) { - token = lexer.lexToken(); - if (token.kind != TokenKind::colon) { - emitError(loc, "expected '*' to be followed by ':'"); - return {}; - } - return SequenceType::Shape(); - } - while (true) { - if (token.kind != TokenKind::eroteme) { - goto shape_spec; - } - bounds.emplace_back(SequenceType::Extent()); - goto check_xchar; - shape_spec: - if (token.kind != TokenKind::intlit) { - emitError(loc, "expected an integer or '?' in shape specification"); - return {}; - } - token.text.getAsInteger(10, extent); - bounds.emplace_back(extent); - check_xchar: - nextChar = lexer.nextNonWSChar(); - if (nextChar == ':') { - return SequenceType::Shape(bounds); - } - if (nextChar != 'x') { - emitError(loc, "expected an 'x' or ':' after integer"); - return {}; - } - token = lexer.lexToken(); - } +// `tdesc` `<` type `>` +TypeDescType parseTypeDesc(M::DialectAsmParser &parser, M::Location loc) { + return parseTypeSingleton(parser, loc); } -// affine-map ::= `#` ident -M::AffineMapAttr FIRTypeParser::parseAffineMapAttr() { - return {}; // FIXME opParser->parseAttr(); +// `void` +M::Type parseVoid(M::DialectAsmParser &parser) { + return parser.getBuilder().getNoneType(); } -// bounds ::= lo extent stride | `?` -// `array` `<` bounds (`,` bounds)* `:` type (',' affine-map)? `>` -SequenceType FIRTypeParser::parseSequence() { - if (consumeToken(TokenKind::leftang, "expected '<' in array type")) { - return {}; - } - auto shape = parseShape(); - M::Type eleTy = parseNextType(); - if (!eleTy) { - emitError(loc, "invalid element type"); +// `array` `<` `*` | bounds (`x` bounds)* `:` type (',' affine-map)? `>` +// bounds ::= `?` | int-lit +SequenceType parseSequence(M::DialectAsmParser &parser, M::Location) { + if (parser.parseLess()) { + parser.emitError(parser.getNameLoc(), "expecting '<'"); return {}; } - auto token = lexer.lexToken(); - M::AffineMapAttr map; - if (token.kind == TokenKind::comma) { - map = parseAffineMapAttr(); - token = lexer.lexToken(); - } - if (token.kind != TokenKind::rightang) { - emitError(loc, "expected '>' in array type"); + 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 {}; } - if (checkAtEnd()) { + M::Type eleTy; + if (parser.parseType(eleTy) || parser.parseGreater()) { + parser.emitError(parser.getNameLoc(), "expecting element type"); return {}; } - return SequenceType::get(shape, eleTy, map); -} - -// Parses: string `:` type (',' string `:` type)* '}' -RecordType::TypeList FIRTypeParser::parseTypeList() { - RecordType::TypeList result; - while (true) { - auto name{lexer.lexToken()}; - if (name.kind != TokenKind::ident) { - emitError(loc, "expected identifier"); - return {}; - } - if (consumeToken(TokenKind::colon, "expected colon")) { - return {}; - } - auto memTy{parseNextType()}; - result.emplace_back(name.text, memTy); - auto token{lexer.lexToken()}; - if (token.kind == TokenKind::rightbrace) { - return result; - } - if (token.kind != TokenKind::comma) { - emitError(loc, "expected ','"); - return {}; - } - } -} - -// Parses: string `:` int-type (',' string `:` int-type)* ')' -RecordType::TypeList FIRTypeParser::parseLenParamList() { - RecordType::TypeList result; - while (true) { - auto name{lexer.lexToken()}; - if (name.kind != TokenKind::ident) { - emitError(loc, "expected identifier"); - return {}; - } - if (consumeToken(TokenKind::colon, "expected colon")) { - return {}; - } - auto memTy{parseNextType()}; - result.emplace_back(name.text, memTy); - auto token{lexer.lexToken()}; - if (token.kind == TokenKind::rightparen) { - return result; - } - if (token.kind != TokenKind::comma) { - emitError(loc, "expected ','"); + M::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(M::Type ty) { @@ -615,8 +195,8 @@ bool verifyIntegerType(M::Type ty) { bool verifyRecordMemberType(M::Type ty) { return !(ty.dyn_cast() || ty.dyn_cast() || ty.dyn_cast() || ty.dyn_cast() || - ty.dyn_cast() || ty.dyn_cast() || - ty.dyn_cast()); + ty.dyn_cast() || ty.dyn_cast() || + ty.dyn_cast() || ty.dyn_cast()); } bool verifySameLists(L::ArrayRef a1, @@ -632,34 +212,34 @@ bool verifySameLists(L::ArrayRef a1, return true; } -RecordType -FIRTypeParser::verifyDerived(RecordType derivedTy, - L::ArrayRef lenPList, - L::ArrayRef typeList) { +RecordType verifyDerived(M::DialectAsmParser &parser, RecordType derivedTy, + L::ArrayRef lenPList, + L::ArrayRef typeList) { + auto loc = parser.getNameLoc(); if (!verifySameLists(derivedTy.getLenParamList(), lenPList) || !verifySameLists(derivedTy.getTypeList(), typeList)) { - emitError(loc, "cannot redefine record type members"); + parser.emitError(loc, "cannot redefine record type members"); return {}; } for (auto &p : lenPList) if (!verifyIntegerType(p.second)) { - emitError(loc, "LEN parameter must be integral type"); + parser.emitError(loc, "LEN parameter must be integral type"); return {}; } for (auto &p : typeList) if (!verifyRecordMemberType(p.second)) { - emitError(loc, "field parameter has invalid type"); + parser.emitError(loc, "field parameter has invalid type"); return {}; } llvm::StringSet<> uniq; for (auto &p : lenPList) if (!uniq.insert(p.first).second) { - emitError(loc, "LEN parameter cannot have duplicate name"); + parser.emitError(loc, "LEN parameter cannot have duplicate name"); return {}; } for (auto &p : typeList) if (!uniq.insert(p.first).second) { - emitError(loc, "field cannot have duplicate name"); + parser.emitError(loc, "field cannot have duplicate name"); return {}; } return derivedTy; @@ -669,91 +249,65 @@ FIRTypeParser::verifyDerived(RecordType derivedTy, // `type` `<` name // (`(` id `:` type (`,` id `:` type)* `)`)? // (`{` id `:` type (`,` id `:` type)* `}`)? '>' -RecordType FIRTypeParser::parseDerived() { - if (consumeToken(TokenKind::leftang, "expected '<' in type type")) { - return {}; - } - auto name{lexer.lexToken()}; - if (name.kind != TokenKind::ident) { - emitError(loc, "expected a identifier as name of derived type"); +RecordType parseDerived(M::DialectAsmParser &parser, M::Location) { + L::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(getContext(), name.text); - auto token = lexer.lexToken(); + RecordType result = RecordType::get(parser.getBuilder().getContext(), name); + RecordType::TypeList lenParamList; - RecordType::TypeList typeList; - if (token.kind == TokenKind::leftbrace) { - goto parse_fields; - } else if (token.kind != TokenKind::leftparen) { - // degenerate case? - goto check_close; - } - lenParamList = parseLenParamList(); - token = lexer.lexToken(); - if (token.kind != TokenKind::leftbrace) { - // no fields? - goto check_close; + if (!parser.parseOptionalLParen()) { + while (true) { + L::StringRef lenparam; + M::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 {}; + } } -parse_fields: - typeList = parseTypeList(); - token = lexer.lexToken(); -check_close: - if (token.kind != TokenKind::rightang) { - emitError(loc, "expected '>' in type type"); - return {}; + + RecordType::TypeList typeList; + if (!parser.parseOptionalLBrace()) { + while (true) { + L::StringRef field; + M::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 (checkAtEnd()) { + + 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(result, lenParamList, typeList); -} - -M::Type FIRTypeParser::parseType() { - auto_counter c{recursiveCall}; - auto token = lexer.lexToken(); - if (token.kind == TokenKind::ident) { - if (token.text == "ref") - return parseReference(); - if (token.text == "array") - return parseSequence(); - if (token.text == "char") - return parseCharacter(); - if (token.text == "logical") - return parseLogical(); - if (token.text == "real") - return parseReal(); - if (token.text == "type") - return parseDerived(); - if (token.text == "box") - return parseBox(); - if (token.text == "boxchar") - return parseBoxChar(); - if (token.text == "boxproc") - return parseBoxProc(); - if (token.text == "ptr") - return parsePointer(); - if (token.text == "heap") - return parseHeap(); - if (token.text == "dims") - return parseDims(); - if (token.text == "tdesc") - return parseTypeDesc(); - if (token.text == "field") - return parseField(); - if (token.text == "int") - return parseInteger(); - if (token.text == "complex") - return parseComplex(); - if (token.text == "void") - return parseVoid(); - emitError(loc, "not a known fir type"); - return {}; - } - emitError(loc, "invalid token"); - return {}; + return verifyDerived(parser, result, lenParamList, typeList); } // !fir.ptr and !fir.heap where X is !fir.ptr, !fir.heap, or !fir.ref @@ -765,6 +319,55 @@ bool singleIndirectionLevel(M::Type ty) { } // namespace +// Implementation of the thin interface from dialect to type parser + +M::Type fir::parseFirType(FIROpsDialect *, M::DialectAsmParser &parser) { + L::StringRef typeNameLit; + if (M::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 { @@ -838,6 +441,24 @@ struct FieldTypeStorage : public M::TypeStorage { explicit FieldTypeStorage(KindTy) {} }; +/// The type of a derived type LEN parameter reference +struct LenTypeStorage : public M::TypeStorage { + using KeyTy = KindTy; + + static unsigned hashKey(const KeyTy &) { return L::hash_combine(0); } + + bool operator==(const KeyTy &) const { return true; } + + static LenTypeStorage *construct(M::TypeStorageAllocator &allocator, KindTy) { + auto *storage = allocator.allocate(); + return new (storage) LenTypeStorage{0}; + } + +private: + LenTypeStorage() = delete; + explicit LenTypeStorage(KindTy) {} +}; + /// `LOGICAL` storage struct LogicalTypeStorage : public M::TypeStorage { using KeyTy = KindTy; @@ -1250,6 +871,12 @@ FieldType fir::FieldType::get(M::MLIRContext *ctxt, KindTy) { return Base::get(ctxt, FIR_FIELD, 0); } +// Len + +LenType fir::LenType::get(M::MLIRContext *ctxt, KindTy) { + return Base::get(ctxt, FIR_LEN, 0); +} + // LOGICAL LogicalType fir::LogicalType::get(M::MLIRContext *ctxt, KindTy kind) { @@ -1342,7 +969,8 @@ M::Type fir::ReferenceType::getEleTy() const { M::LogicalResult fir::ReferenceType::verifyConstructionInvariants( L::Optional loc, M::MLIRContext *context, M::Type eleTy) { if (eleTy.dyn_cast() || eleTy.dyn_cast() || - eleTy.dyn_cast() || eleTy.dyn_cast()) + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast()) return M::failure(); return M::success(); } @@ -1365,9 +993,9 @@ M::LogicalResult fir::PointerType::verifyConstructionInvariants( L::Optional loc, M::MLIRContext *context, M::Type eleTy) { if (eleTy.dyn_cast() || eleTy.dyn_cast() || eleTy.dyn_cast() || eleTy.dyn_cast() || - eleTy.dyn_cast() || eleTy.dyn_cast() || - eleTy.dyn_cast() || eleTy.dyn_cast() || - eleTy.dyn_cast()) + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast()) return M::failure(); return M::success(); } @@ -1388,9 +1016,9 @@ M::LogicalResult fir::HeapType::verifyConstructionInvariants( L::Optional loc, M::MLIRContext *context, M::Type eleTy) { if (eleTy.dyn_cast() || eleTy.dyn_cast() || eleTy.dyn_cast() || eleTy.dyn_cast() || - eleTy.dyn_cast() || eleTy.dyn_cast() || - eleTy.dyn_cast() || eleTy.dyn_cast() || - eleTy.dyn_cast()) + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast()) return M::failure(); return M::success(); } @@ -1421,9 +1049,10 @@ M::LogicalResult fir::SequenceType::verifyConstructionInvariants( // DIMENSION attribute can only be applied to an intrinsic or record type if (eleTy.dyn_cast() || eleTy.dyn_cast() || eleTy.dyn_cast() || eleTy.dyn_cast() || - eleTy.dyn_cast() || eleTy.dyn_cast() || - eleTy.dyn_cast() || eleTy.dyn_cast() || - eleTy.dyn_cast() || eleTy.dyn_cast()) + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast()) return M::failure(); return M::success(); } @@ -1431,37 +1060,18 @@ M::LogicalResult fir::SequenceType::verifyConstructionInvariants( // compare if two shapes are equivalent bool fir::operator==(const SequenceType::Shape &sh_1, const SequenceType::Shape &sh_2) { - if (sh_1.hasValue() != sh_2.hasValue()) { - return false; - } - if (!sh_1.hasValue()) { - return true; - } - auto &bnd_1 = *sh_1; - auto &bnd_2 = *sh_2; - if (bnd_1.size() != bnd_2.size()) { + if (sh_1.size() != sh_2.size()) return false; - } - for (std::size_t i = 0, end = bnd_1.size(); i != end; ++i) { - if (bnd_1[i].hasValue() != bnd_2[i].hasValue()) { - return false; - } - if (bnd_1[i].hasValue() && *bnd_1[i] != *bnd_2[i]) { + 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 an Extent -L::hash_code fir::hash_value(const SequenceType::Extent &ext) { - return L::hash_combine(ext.hasValue() ? *ext : 0); -} - // compute the hash of a Shape L::hash_code fir::hash_value(const SequenceType::Shape &sh) { - if (sh.hasValue()) { - return L::hash_combine_range(sh->begin(), sh->end()); + if (sh.size()) { + return L::hash_combine_range(sh.begin(), sh.end()); } return L::hash_combine(0); } @@ -1500,6 +1110,14 @@ M::LogicalResult fir::RecordType::verifyConstructionInvariants( return M::success(); } +M::Type fir::RecordType::getType(L::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) @@ -1515,138 +1133,137 @@ M::LogicalResult fir::TypeDescType::verifyConstructionInvariants( L::Optional loc, M::MLIRContext *context, M::Type eleTy) { if (eleTy.dyn_cast() || eleTy.dyn_cast() || eleTy.dyn_cast() || eleTy.dyn_cast() || - eleTy.dyn_cast() || eleTy.dyn_cast() || - eleTy.dyn_cast()) + eleTy.dyn_cast() || eleTy.dyn_cast() || + eleTy.dyn_cast() || eleTy.dyn_cast()) return M::failure(); return M::success(); } -// Implementation of the thin interface from dialect to type parser +namespace { -M::Type fir::parseFirType(FIROpsDialect *dialect, L::StringRef rawData, - M::Location loc) { - FIRTypeParser parser{dialect, rawData, loc}; - return parser.parseType(); +void printBounds(llvm::raw_ostream &os, const SequenceType::Shape &bounds) { + os << '<'; + for (auto &b : bounds) { + if (b >= 0) { + os << b << 'x'; + } else { + os << "?x"; + } + } } -namespace { -class TypePrinter { -public: - void print(FIROpsDialect *dialect, M::Type ty, llvm::raw_ostream &os) { - if (auto type = ty.dyn_cast()) { - os << "ref<"; - type.getEleTy().print(os); - os << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "logical<" << type.getFKind() << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "real<" << type.getFKind() << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "char<" << type.getFKind() << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "tdesc<"; - type.getOfTy().print(os); - os << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "field"; - } else if (auto type = ty.dyn_cast()) { - os << "box<"; - type.getEleTy().print(os); - if (auto map = type.getLayoutMap()) { - os << ", "; - map.print(os); - } - os << '>'; - } else if (auto type = ty.dyn_cast()) { - auto eleTy = type.getEleTy().cast(); - os << "boxchar<" << eleTy.getFKind() << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "boxproc<"; - type.getEleTy().print(os); - os << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "dims<" << type.getRank() << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "array"; - auto shape = type.getShape(); - if (shape.hasValue()) { - printBounds(os, *shape); - } else { - os << "<*"; - } - os << ':'; - type.getEleTy().print(os); - if (auto map = type.getLayoutMap()) { - os << ", "; - map.print(os); - } - os << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "heap<"; - type.getEleTy().print(os); - os << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "ptr<"; - type.getEleTy().print(os); - os << '>'; - } else if (auto type = ty.dyn_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 << ')'; +llvm::SmallPtrSet recordTypeVisited; + +} // namespace + +void fir::printFirType(FIROpsDialect *, M::Type ty, M::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 = ','; } - if (type.getTypeList().size()) { - char ch = '{'; - for (auto p : type.getTypeList()) { - os << ch << p.first << ':'; - p.second.print(os); - ch = ','; - } - os << '}'; + os << ')'; + } + if (type.getTypeList().size()) { + char ch = '{'; + for (auto p : type.getTypeList()) { + os << ch << p.first << ':'; + p.second.print(os); + ch = ','; } - recordTypeVisited.erase(type.uniqueKey()); + os << '}'; } - os << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "int<" << type.getFKind() << '>'; - } else if (auto type = ty.dyn_cast()) { - os << "complex<" << type.getFKind() << '>'; + 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 { - assert(false); + os << "<*:"; } - } - -private: - void printBounds(llvm::raw_ostream &os, const SequenceType::Bounds &bounds) { - char ch = '<'; - for (auto &b : bounds) { - if (b.hasValue()) { - os << ch << *b; - } else { - os << ch << '?'; - } - ch = 'x'; + 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; } - - // must be in a global context because the printer must be able to track - // context through multiple recursive invocations of the mlir type printer - static llvm::SmallPtrSet - recordTypeVisited; -}; - -llvm::SmallPtrSet - TypePrinter::recordTypeVisited; // instantiate -} // namespace - -void fir::printFirType(FIROpsDialect *dialect, M::Type ty, - llvm::raw_ostream &os) { - TypePrinter().print(dialect, ty, os); } diff --git a/lib/fir/InternalNames.cpp b/lib/fir/InternalNames.cpp new file mode 100644 index 000000000000..3f7657a462ba --- /dev/null +++ b/lib/fir/InternalNames.cpp @@ -0,0 +1,141 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fir/InternalNames.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" + +namespace L = llvm; + +namespace { + +inline L::Twine prefix() { return "_Q"; } + +L::Twine doModules(L::ArrayRef mods) { + L::Twine result; + auto *token = "M"; + for (auto mod : mods) { + result.concat(token).concat(mod); + token = "S"; + } + return result; +} + +L::Twine doModulesHost(L::ArrayRef mods, + L::Optional host) { + L::Twine result = doModules(mods); + if (host.hasValue()) + result.concat("F").concat(*host); + return result; +} + +} // namespace + +L::Twine fir::NameMangler::toLower(L::StringRef name) { + auto lo = name.lower(); + if (name.equals(lo)) + return name; + return cache.insert(lo).first->getKey(); +} + +L::Twine fir::NameMangler::addAsString(std::int64_t i) { + return cache.insert(std::to_string(i)).first->getKey(); +} + +L::Twine fir::NameMangler::doKind(std::int64_t kind) { + if (kind < 0) + return "KN" + addAsString(-kind); + return "K" + addAsString(kind); +} + +L::Twine fir::NameMangler::doKinds(L::ArrayRef kinds) { + L::Twine result; + for (auto i : kinds) + result.concat(doKind(i)); + return result; +} + +L::Twine fir::NameMangler::doCommonBlock(L::StringRef name) { + return prefix() + "B" + toLower(name); +} + +L::Twine fir::NameMangler::doConstant(L::ArrayRef modules, + L::StringRef name) { + return prefix() + doModules(modules) + "EC" + toLower(name); +} + +L::Twine fir::NameMangler::doDispatchTable(L::ArrayRef modules, + L::Optional host, + L::StringRef name, + L::ArrayRef kinds) { + return prefix() + doModulesHost(modules, host) + "DT" + toLower(name) + + doKinds(kinds); +} + +L::Twine fir::NameMangler::doGenerated(L::StringRef name) { + return prefix() + "Q" + toLower(name); +} + +L::Twine fir::NameMangler::doIntrinsicTypeDescriptor( + L::ArrayRef modules, L::Optional host, + IntrinsicType type, std::int64_t kind) { + char const *name; + switch (type) { + case IntrinsicType::CHARACTER: + name = "character"; + break; + case IntrinsicType::COMPLEX: + name = "complex"; + break; + case IntrinsicType::INTEGER: + name = "integer"; + break; + case IntrinsicType::LOGICAL: + name = "logical"; + break; + case IntrinsicType::REAL: + name = "real"; + break; + } + return prefix() + doModulesHost(modules, host) + "C" + name + doKind(kind); +} + +L::Twine fir::NameMangler::doProcedure(L::ArrayRef modules, + L::Optional host, + L::StringRef name) { + return prefix() + doModulesHost(modules, host) + "P" + toLower(name); +} + +L::Twine fir::NameMangler::doType(L::ArrayRef modules, + L::Optional host, + L::StringRef name, + L::ArrayRef kinds) { + return prefix() + doModulesHost(modules, host) + "T" + toLower(name) + + doKinds(kinds); +} + +L::Twine fir::NameMangler::doTypeDescriptor(L::ArrayRef modules, + L::Optional host, + L::StringRef name, + L::ArrayRef kinds) { + return prefix() + doModulesHost(modules, host) + "CT" + toLower(name) + + doKinds(kinds); +} + +L::Twine fir::NameMangler::doVariable(L::ArrayRef modules, + L::StringRef name) { + return prefix() + doModules(modules) + "E" + toLower(name); +} diff --git a/lib/fir/StdConverter.cpp b/lib/fir/StdConverter.cpp index 9736b8b94fc0..e25fbda94baa 100644 --- a/lib/fir/StdConverter.cpp +++ b/lib/fir/StdConverter.cpp @@ -13,9 +13,10 @@ // limitations under the License. #include "fir/Transforms/StdConverter.h" -#include "fir/Dialect.h" +#include "fir/FIRDialect.h" #include "fir/FIROps.h" -#include "fir/Type.h" +#include "fir/FIRType.h" +#include "mlir/Conversion/AffineToStandard/AffineToStandard.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h" #include "mlir/Dialect/AffineOps/AffineOps.h" @@ -24,8 +25,6 @@ #include "mlir/IR/StandardTypes.h" #include "mlir/Pass/Pass.h" #include "mlir/Target/LLVMIR.h" -#include "mlir/Transforms/DialectConversion.h" -#include "mlir/Transforms/LowerAffine.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Config/abi-breaking.h" #include "llvm/IR/IRBuilder.h" @@ -42,6 +41,11 @@ namespace M = mlir; using namespace fir; +static L::cl::opt + ClDisableFirToStd("disable-fir2std", + L::cl::desc("disable FIR to standard pass"), + L::cl::init(false), L::cl::Hidden); + namespace { using SmallVecResult = L::SmallVector; @@ -144,7 +148,10 @@ class FIRToStdLoweringPass : public M::ModulePass { public: void runOnModule() override { - return; + if (ClDisableFirToStd) + return; + + return; // FIXME for (auto fn : getModule().getOps()) { M::OpBuilder rewriter{&fn.getBody()}; diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index 1ba031a454a4..36db1a3dbe9d 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -13,10 +13,11 @@ // limitations under the License. #include "fir/Tilikum/Tilikum.h" -#include "fir/Dialect.h" +#include "fir/Attribute.h" +#include "fir/FIRDialect.h" #include "fir/FIROps.h" +#include "fir/FIRType.h" #include "fir/KindMapping.h" -#include "fir/Type.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h" #include "mlir/Dialect/AffineOps/AffineOps.h" @@ -31,11 +32,16 @@ #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/raw_ostream.h" -/// The bridge that performs the conversion of FIR and standard dialect -/// operations to the LLVM-IR dialect. +/// The Tilikum bridge performs the conversion of operations from both the FIR +/// and standard dialects to the LLVM-IR dialect. +/// +/// Some FIR operations may be lowered to other dialects, such as standard, but +/// some FIR operations will pass through to the Tilikum bridge. This may be +/// necessary to preserve the semantics of the Fortran program. #undef TODO #define TODO(X) \ @@ -45,6 +51,15 @@ namespace L = llvm; namespace M = mlir; +static L::cl::opt + ClDisableFirToLLVMIR("disable-fir2llvmir", + L::cl::desc("disable FIR to LLVM-IR dialect pass"), + L::cl::init(false), L::cl::Hidden); + +static L::cl::opt ClDisableLLVM("disable-llvm", + L::cl::desc("disable LLVM pass"), + L::cl::init(false), L::cl::Hidden); + using namespace fir; namespace { @@ -53,6 +68,8 @@ using SmallVecResult = L::SmallVector; using OperandTy = L::ArrayRef; using AttributeTy = L::ArrayRef; +const unsigned defaultAlign = 8; + /// FIR type converter /// This converts FIR types to LLVM types (for now) class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { @@ -63,20 +80,24 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { FIRToLLVMTypeConverter(M::MLIRContext *context) : LLVMTypeConverter(context), kindMapping(context) {} + // This returns the type of a single column. Rows are added by the caller. // fir.dims --> llvm<"[r x [3 x i64]]"> - // FIXME M::LLVM::LLVMType dimsType() { auto i64Ty{M::LLVM::LLVMType::getInt64Ty(llvmDialect)}; return M::LLVM::LLVMType::getArrayTy(i64Ty, 3); } // i32 is used here because LLVM wants i32 constants when indexing into struct - // types. Indexing into other aggregate types is more flexible. TODO: See if - // we can't use i64 anyway; the restiction may not longer hold. - M::LLVM::LLVMType indexType() { + // types. Indexing into other aggregate types is more flexible. + M::LLVM::LLVMType offsetType() { return M::LLVM::LLVMType::getInt32Ty(llvmDialect); } + // i64 can be used to index into aggregates like arrays + M::LLVM::LLVMType indexType() { + return M::LLVM::LLVMType::getInt64Ty(llvmDialect); + } + // This corresponds to the descriptor as defined ISO_Fortran_binding.h and the // addendum defined in descriptor.h. // FIXME: This code should be generated and follow SPOT @@ -166,9 +187,10 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { return fromRealTypeID(kindMapping.getRealTypeID(kind), kind); } - // The cache is needed to keep a unique mapping from name -> StructType + // fir.type --> llvm<"%name = { ty... }"> M::LLVM::LLVMType convertRecordType(RecordType derived) { auto name{derived.getName()}; + // The cache is needed to keep a unique mapping from name -> StructType auto iter{identStructCache.find(name)}; if (iter != identStructCache.end()) return iter->second; @@ -184,17 +206,28 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { // fir.array --> llvm<"[...[c x any]]"> M::LLVM::LLVMType convertSequenceType(SequenceType seq) { auto baseTy = unwrap(convertType(seq.getEleTy())); - if (auto shape = seq.getShape()) { - for (auto e : shape.getValue()) - if (e.hasValue()) - baseTy = M::LLVM::LLVMType::getArrayTy(baseTy, e.getValue()); - else - return baseTy.getPointerTo(); + auto shape = seq.getShape(); + if (shape.size()) { + for (auto e : shape) { + if (e < 0) + e = 0; + baseTy = M::LLVM::LLVMType::getArrayTy(baseTy, e); + } return baseTy; } return baseTy.getPointerTo(); } + // tuple --> llvm<"{ ts... }"> + M::LLVM::LLVMType convertTupleType(M::TupleType tuple) { + L::SmallVector inMembers; + tuple.getFlattenedTypes(inMembers); + L::SmallVector members; + for (auto mem : inMembers) + members.push_back(convertType(mem).cast()); + return M::LLVM::LLVMType::getStructTy(llvmDialect, members); + } + // fir.tdesc --> llvm<"i8*"> // FIXME: for now use a void*, however pointer identity is not sufficient for // the f18 object v. class distinction @@ -219,11 +252,13 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { if (auto dims = t.dyn_cast()) return M::LLVM::LLVMType::getArrayTy(dimsType(), dims.getRank()); if (auto field = t.dyn_cast()) - return M::LLVM::LLVMType::getInt64Ty(llvmDialect); + return M::LLVM::LLVMType::getInt32Ty(llvmDialect); if (auto heap = t.dyn_cast()) return convertPointerLike(heap); if (auto integer = t.dyn_cast()) return convertIntegerType(integer); + if (auto field = t.dyn_cast()) + return M::LLVM::LLVMType::getInt32Ty(llvmDialect); if (auto logical = t.dyn_cast()) return convertLogicalType(logical); if (auto pointer = t.dyn_cast()) @@ -236,6 +271,10 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { return convertSequenceType(sequence); if (auto tdesc = t.dyn_cast()) return convertTypeDescType(tdesc.getContext()); + if (auto tuple = t.dyn_cast()) + return convertTupleType(tuple); + if (auto none = t.dyn_cast()) + return M::LLVM::LLVMType::getStructTy(llvmDialect, {}); return LLVMTypeConverter::convertType(t); } @@ -275,6 +314,7 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { // instantiate static data member L::StringMap FIRToLLVMTypeConverter::identStructCache; +/// remove `omitNames` (by name) from the attribute dictionary L::SmallVector pruneNamedAttrDict(L::ArrayRef attrs, L::ArrayRef omitNames) { @@ -292,6 +332,10 @@ pruneNamedAttrDict(L::ArrayRef attrs, return result; } +inline M::LLVM::LLVMType getVoidPtrType(M::LLVM::LLVMDialect *dialect) { + return M::LLVM::LLVMType::getInt8PtrTy(dialect); +} + /// FIR conversion pattern template template class FIROpConversion : public M::ConversionPattern { @@ -304,6 +348,17 @@ class FIROpConversion : public M::ConversionPattern { protected: L::LLVMContext &getLLVMContext() const { return lowering.getLLVMContext(); } M::LLVM::LLVMDialect *getDialect() const { return lowering.getDialect(); } + M::Type convertType(M::Type ty) const { return lowering.convertType(ty); } + M::LLVM::LLVMType unwrap(M::Type ty) const { return lowering.unwrap(ty); } + M::LLVM::LLVMType voidPtrTy() const { return getVoidPtrType(getDialect()); } + + M::LLVM::ConstantOp genConstantOffset(M::Location loc, + M::ConversionPatternRewriter &rewriter, + int offset) const { + auto ity = lowering.offsetType(); + auto cattr = rewriter.getI32IntegerAttr(offset); + return rewriter.create(loc, ity, cattr); + } FIRToLLVMTypeConverter &lowering; }; @@ -315,15 +370,22 @@ struct AddrOfOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto addr = M::cast(op); - auto ty = lowering.unwrap(lowering.convertType(addr.getType())); + auto ty = unwrap(convertType(addr.getType())); auto attrs = pruneNamedAttrDict(addr.getAttrs(), {"symbol"}); - rewriter.replaceOpWithNewOp(addr, ty, addr.symbol(), - attrs); + rewriter.replaceOpWithNewOp( + addr, ty, addr.symbol().getRootReference(), attrs); return matchSuccess(); } }; -// convert to LLVM IR dialect `alloca` +M::LLVM::ConstantOp genConstantIndex(M::Location loc, M::LLVM::LLVMType ity, + M::ConversionPatternRewriter &rewriter, + int offset) { + auto cattr = rewriter.getI64IntegerAttr(offset); + return rewriter.create(loc, ity, cattr); +} + +/// convert to LLVM IR dialect `alloca` struct AllocaOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -332,11 +394,13 @@ struct AllocaOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto alloc = M::cast(op); auto loc = alloc.getLoc(); - auto ty = lowering.convertType(alloc.getType()); auto ity = lowering.indexType(); - auto c1attr = rewriter.getI32IntegerAttr(1); - auto c1 = rewriter.create(loc, ity, c1attr); - rewriter.replaceOpWithNewOp(alloc, ty, c1.getResult(), + auto c1 = genConstantIndex(loc, ity, rewriter, 1); + auto *size = c1.getResult(); + for (auto *opnd : operands) + size = rewriter.create(loc, ity, size, opnd); + auto ty = convertType(alloc.getType()); + rewriter.replaceOpWithNewOp(alloc, ty, size, alloc.getAttrs()); return matchSuccess(); } @@ -346,21 +410,17 @@ M::LLVM::LLVMFuncOp getMalloc(AllocMemOp op, M::ConversionPatternRewriter &rewriter, M::LLVM::LLVMDialect *dialect) { auto module = op.getParentOfType(); - auto mallocFunc = module.lookupSymbol("malloc"); - if (!mallocFunc) { - M::OpBuilder moduleBuilder( - op.getParentOfType().getBodyRegion()); - auto voidPtrType = M::LLVM::LLVMType::getInt8PtrTy(dialect); - auto indexType = M::LLVM::LLVMType::getInt64Ty(dialect); - mallocFunc = moduleBuilder.create( - rewriter.getUnknownLoc(), "malloc", - M::LLVM::LLVMType::getFunctionTy(voidPtrType, indexType, - /*isVarArg=*/false)); - } - return mallocFunc; + if (auto mallocFunc = module.lookupSymbol("malloc")) + return mallocFunc; + M::OpBuilder moduleBuilder(op.getParentOfType().getBodyRegion()); + auto indexType = M::LLVM::LLVMType::getInt64Ty(dialect); + return moduleBuilder.create( + rewriter.getUnknownLoc(), "malloc", + M::LLVM::LLVMType::getFunctionTy(getVoidPtrType(dialect), indexType, + /*isVarArg=*/false)); } -// convert to `call` to the runtime to `malloc` memory +/// convert to `call` to the runtime to `malloc` memory struct AllocMemOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -368,18 +428,67 @@ struct AllocMemOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto heap = M::cast(op); - auto ty = lowering.convertType(heap.getType()); - // FIXME: should be a call to malloc + auto ty = convertType(heap.getType()); + auto dialect = getDialect(); + auto mallocFunc = getMalloc(heap, rewriter, dialect); auto loc = heap.getLoc(); auto ity = lowering.indexType(); - auto c1attr = rewriter.getI32IntegerAttr(1); - auto c1 = rewriter.create(loc, ity, c1attr); - rewriter.replaceOpWithNewOp(heap, ty, c1.getResult(), - heap.getAttrs()); + auto c1 = genConstantIndex(loc, ity, rewriter, 1); + auto *size = c1.getResult(); + for (auto *opnd : operands) + size = rewriter.create(loc, ity, size, opnd); + heap.setAttr("callee", rewriter.getSymbolRefAttr(mallocFunc)); + L::SmallVector args{size}; + rewriter.replaceOpWithNewOp(heap, ty, args, + heap.getAttrs()); + return matchSuccess(); + } +}; + +/// obtain the free() function +M::LLVM::LLVMFuncOp getFree(FreeMemOp op, + M::ConversionPatternRewriter &rewriter, + M::LLVM::LLVMDialect *dialect) { + auto module = op.getParentOfType(); + if (auto freeFunc = module.lookupSymbol("free")) + return freeFunc; + M::OpBuilder moduleBuilder(op.getParentOfType().getBodyRegion()); + auto voidType = M::LLVM::LLVMType::getVoidTy(dialect); + return moduleBuilder.create( + rewriter.getUnknownLoc(), "free", + M::LLVM::LLVMType::getFunctionTy(voidType, getVoidPtrType(dialect), + /*isVarArg=*/false)); +} + +/// lower a freemem instruction into a call to free() +struct FreeMemOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto freemem = M::cast(op); + auto dialect = getDialect(); + auto freeFunc = getFree(freemem, rewriter, dialect); + auto bitcast = rewriter.create( + freemem.getLoc(), voidPtrTy(), operands[0]); + freemem.setAttr("callee", rewriter.getSymbolRefAttr(freeFunc)); + rewriter.replaceOpWithNewOp( + freemem, M::LLVM::LLVMType::getVoidTy(dialect), + L::SmallVector{bitcast}, freemem.getAttrs()); return matchSuccess(); } }; +template +M::LLVM::GEPOp genGEP(M::Location loc, M::LLVM::LLVMType ty, + M::ConversionPatternRewriter &rewriter, M::Value *base, + ARGS... args) { + L::SmallVector cv{args...}; + return rewriter.create(loc, ty, base, cv); +} + +/// convert to returning the first element of the box (any flavor) struct BoxAddrOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -389,16 +498,15 @@ struct BoxAddrOpConversion : public FIROpConversion { auto boxaddr = M::cast(op); auto a = operands[0]; auto loc = boxaddr.getLoc(); - auto ty = lowering.convertType(boxaddr.getType()); - auto c0attr = rewriter.getI32IntegerAttr(0); + auto ty = convertType(boxaddr.getType()); if (auto argty = boxaddr.val()->getType().dyn_cast()) { - auto ity = lowering.indexType(); - auto c0 = rewriter.create(loc, ity, c0attr); - L::SmallVector args({a, c0, c0}); - auto pty = lowering.unwrap(ty).getPointerTo(); - auto p = rewriter.create(loc, pty, args); + auto c0 = genConstantOffset(loc, rewriter, 0); + auto pty = unwrap(ty).getPointerTo(); + auto p = genGEP(loc, unwrap(pty), rewriter, a, c0, c0); + // load the pointer from the buffer rewriter.replaceOpWithNewOp(boxaddr, ty, p); } else { + auto c0attr = rewriter.getI32IntegerAttr(0); auto c0 = M::ArrayAttr::get(c0attr, boxaddr.getContext()); rewriter.replaceOpWithNewOp(boxaddr, ty, a, c0); } @@ -406,6 +514,7 @@ struct BoxAddrOpConversion : public FIROpConversion { } }; +/// convert to an extractvalue for the 2nd part of the boxchar struct BoxCharLenOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -414,7 +523,7 @@ struct BoxCharLenOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto boxchar = M::cast(op); auto a = operands[0]; - auto ty = lowering.convertType(boxchar.getType()); + auto ty = convertType(boxchar.getType()); auto ctx = boxchar.getContext(); auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); rewriter.replaceOpWithNewOp(boxchar, ty, a, c1); @@ -422,6 +531,7 @@ struct BoxCharLenOpConversion : public FIROpConversion { } }; +/// convert to a triple set of GEPs and loads struct BoxDimsOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -432,17 +542,26 @@ struct BoxDimsOpConversion : public FIROpConversion { auto a = operands[0]; auto dim = operands[1]; auto loc = boxdims.getLoc(); - auto ity = lowering.indexType(); - auto c0attr = rewriter.getI32IntegerAttr(0); - auto c0 = rewriter.create(loc, ity, c0attr); - auto c7attr = rewriter.getI32IntegerAttr(7); - auto c7 = rewriter.create(loc, ity, c7attr); - auto ty = lowering.convertType(boxdims.getResult(0)->getType()); - L::SmallVector args({a, c0, c7, dim}); - auto p = rewriter.create(loc, ty, args); - rewriter.replaceOpWithNewOp(boxdims, ty, p); + auto c0 = genConstantOffset(loc, rewriter, 0); + auto c7 = genConstantOffset(loc, rewriter, 7); + auto l0 = loadFromOffset(boxdims, loc, a, c0, c7, dim, 0, rewriter); + auto l1 = loadFromOffset(boxdims, loc, a, c0, c7, dim, 1, rewriter); + auto l2 = loadFromOffset(boxdims, loc, a, c0, c7, dim, 2, rewriter); + rewriter.replaceOp(boxdims, + {l0.getResult(), l1.getResult(), l2.getResult()}); return matchSuccess(); } + + M::LLVM::LoadOp loadFromOffset(BoxDimsOp boxdims, M::Location loc, + M::Value *a, M::LLVM::ConstantOp c0, + M::LLVM::ConstantOp c7, M::Value *dim, int off, + M::ConversionPatternRewriter &rewriter) const { + auto ty = convertType(boxdims.getResult(off)->getType()); + auto pty = unwrap(ty).getPointerTo(); + auto c = genConstantOffset(loc, rewriter, off); + auto p = genGEP(loc, pty, rewriter, a, c0, c7, dim, c); + return rewriter.create(loc, ty, p); + } }; struct BoxEleSizeOpConversion : public FIROpConversion { @@ -454,14 +573,10 @@ struct BoxEleSizeOpConversion : public FIROpConversion { auto boxelesz = M::cast(op); auto a = operands[0]; auto loc = boxelesz.getLoc(); - auto ty = lowering.convertType(boxelesz.getType()); - auto ity = lowering.indexType(); - auto c0attr = rewriter.getI32IntegerAttr(0); - auto c0 = rewriter.create(loc, ity, c0attr); - auto c1attr = rewriter.getI32IntegerAttr(1); - auto c1 = rewriter.create(loc, ity, c1attr); - L::SmallVector args({a, c0, c1}); - auto p = rewriter.create(loc, ty, args); + auto c0 = genConstantOffset(loc, rewriter, 0); + auto c1 = genConstantOffset(loc, rewriter, 1); + auto ty = convertType(boxelesz.getType()); + auto p = genGEP(loc, unwrap(ty), rewriter, a, c0, c1); rewriter.replaceOpWithNewOp(boxelesz, ty, p); return matchSuccess(); } @@ -476,17 +591,13 @@ struct BoxIsAllocOpConversion : public FIROpConversion { auto boxisalloc = M::cast(op); auto a = operands[0]; auto loc = boxisalloc.getLoc(); - auto ty = lowering.convertType(boxisalloc.getType()); - auto ity = lowering.indexType(); - auto c0attr = rewriter.getI32IntegerAttr(0); - auto c0 = rewriter.create(loc, ity, c0attr); - auto c5attr = rewriter.getI32IntegerAttr(5); - auto c5 = rewriter.create(loc, ity, c5attr); - L::SmallVector args({a, c0, c5}); - auto p = rewriter.create(loc, ty, args); + auto ity = lowering.offsetType(); + auto c0 = genConstantOffset(loc, rewriter, 0); + auto c5 = genConstantOffset(loc, rewriter, 5); + auto ty = convertType(boxisalloc.getType()); + auto p = genGEP(loc, unwrap(ty), rewriter, a, c0, c5); auto ld = rewriter.create(loc, ty, p); - auto c2attr = rewriter.getI32IntegerAttr(2); - auto ab = rewriter.create(loc, ity, c2attr); + auto ab = genConstantOffset(loc, rewriter, 2); auto bit = rewriter.create(loc, ity, ld, ab); rewriter.replaceOpWithNewOp( boxisalloc, M::LLVM::ICmpPredicate::ne, bit, c0); @@ -503,14 +614,10 @@ struct BoxIsArrayOpConversion : public FIROpConversion { auto boxisarray = M::cast(op); auto a = operands[0]; auto loc = boxisarray.getLoc(); - auto ty = lowering.convertType(boxisarray.getType()); - auto ity = lowering.indexType(); - auto c0attr = rewriter.getI32IntegerAttr(0); - auto c0 = rewriter.create(loc, ity, c0attr); - auto c3attr = rewriter.getI32IntegerAttr(3); - auto c3 = rewriter.create(loc, ity, c3attr); - L::SmallVector args({a, c0, c3}); - auto p = rewriter.create(loc, ty, args); + auto c0 = genConstantOffset(loc, rewriter, 0); + auto c3 = genConstantOffset(loc, rewriter, 3); + auto ty = convertType(boxisarray.getType()); + auto p = genGEP(loc, unwrap(ty), rewriter, a, c0, c3); auto ld = rewriter.create(loc, ty, p); rewriter.replaceOpWithNewOp( boxisarray, M::LLVM::ICmpPredicate::ne, ld, c0); @@ -527,17 +634,14 @@ struct BoxIsPtrOpConversion : public FIROpConversion { auto boxisptr = M::cast(op); auto a = operands[0]; auto loc = boxisptr.getLoc(); - auto ty = lowering.convertType(boxisptr.getType()); - auto ity = lowering.indexType(); - auto c0attr = rewriter.getI32IntegerAttr(0); - auto c0 = rewriter.create(loc, ity, c0attr); - auto c5attr = rewriter.getI32IntegerAttr(5); - auto c5 = rewriter.create(loc, ity, c5attr); - L::SmallVector args({a, c0, c5}); + auto ty = convertType(boxisptr.getType()); + auto ity = lowering.offsetType(); + auto c0 = genConstantOffset(loc, rewriter, 0); + auto c5 = genConstantOffset(loc, rewriter, 5); + L::SmallVector args{a, c0, c5}; auto p = rewriter.create(loc, ty, args); auto ld = rewriter.create(loc, ty, p); - auto c1attr = rewriter.getI32IntegerAttr(1); - auto ab = rewriter.create(loc, ity, c1attr); + auto ab = genConstantOffset(loc, rewriter, 1); auto bit = rewriter.create(loc, ity, ld, ab); rewriter.replaceOpWithNewOp( boxisptr, M::LLVM::ICmpPredicate::ne, bit, c0); @@ -553,7 +657,7 @@ struct BoxProcHostOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto boxprochost = M::cast(op); auto a = operands[0]; - auto ty = lowering.convertType(boxprochost.getType()); + auto ty = convertType(boxprochost.getType()); auto ctx = boxprochost.getContext(); auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); rewriter.replaceOpWithNewOp(boxprochost, ty, a, @@ -571,14 +675,11 @@ struct BoxRankOpConversion : public FIROpConversion { auto boxrank = M::cast(op); auto a = operands[0]; auto loc = boxrank.getLoc(); - auto ty = lowering.convertType(boxrank.getType()); - auto ity = lowering.indexType(); - auto c0attr = rewriter.getI32IntegerAttr(0); - auto c0 = rewriter.create(loc, ity, c0attr); - auto c3attr = rewriter.getI32IntegerAttr(3); - auto c3 = rewriter.create(loc, ity, c3attr); - L::SmallVector args({a, c0, c3}); - auto pty = lowering.unwrap(ty).getPointerTo(); + auto ty = convertType(boxrank.getType()); + auto c0 = genConstantOffset(loc, rewriter, 0); + auto c3 = genConstantOffset(loc, rewriter, 3); + L::SmallVector args{a, c0, c3}; + auto pty = unwrap(ty).getPointerTo(); auto p = rewriter.create(loc, pty, args); rewriter.replaceOpWithNewOp(boxrank, ty, p); return matchSuccess(); @@ -594,14 +695,11 @@ struct BoxTypeDescOpConversion : public FIROpConversion { auto boxtypedesc = M::cast(op); auto a = operands[0]; auto loc = boxtypedesc.getLoc(); - auto ty = lowering.convertType(boxtypedesc.getType()); - auto ity = lowering.indexType(); - auto c0attr = rewriter.getI32IntegerAttr(0); - auto c0 = rewriter.create(loc, ity, c0attr); - auto c4attr = rewriter.getI32IntegerAttr(4); - auto c4 = rewriter.create(loc, ity, c4attr); - L::SmallVector args({a, c0, c4}); - auto pty = lowering.unwrap(ty).getPointerTo(); + auto ty = convertType(boxtypedesc.getType()); + auto c0 = genConstantOffset(loc, rewriter, 0); + auto c4 = genConstantOffset(loc, rewriter, 4); + L::SmallVector args{a, c0, c4}; + auto pty = unwrap(ty).getPointerTo(); auto p = rewriter.create(loc, pty, args); auto ld = rewriter.create(loc, ty, p); auto i8ptr = M::LLVM::LLVMType::getInt8PtrTy(getDialect()); @@ -610,7 +708,28 @@ struct BoxTypeDescOpConversion : public FIROpConversion { } }; -// direct call LLVM function +struct ConstantOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto constop = M::cast(op); + auto ty_ = constop.getType(); + auto ty = convertType(ty_); + auto attr = constop.getValue(); + auto attr_ = attr.cast(); + if (auto ft = ty_.dyn_cast()) { + auto kind = ft.getFKind(); + L::APFloat f{L::APFloat::IEEEdouble(), attr_.getValue()}; + attr = M::FloatAttr::get(M::FloatType::getF64(constop.getContext()), f); + } + rewriter.replaceOpWithNewOp(constop, ty, attr); + return matchSuccess(); + } +}; + +/// direct call LLVM function struct CallOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -620,14 +739,70 @@ struct CallOpConversion : public FIROpConversion { auto call = M::cast(op); L::SmallVector resultTys; for (auto r : call.getResults()) - resultTys.push_back(lowering.convertType(r->getType())); + resultTys.push_back(convertType(r->getType())); rewriter.replaceOpWithNewOp(call, resultTys, operands, call.getAttrs()); return matchSuccess(); } }; -// convert value of from-type to value of to-type +/// Compare complex values +/// +/// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une). +/// +/// For completeness, all other comparison are done on the real component only. +struct CmpcOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto cmp = M::cast(op); + auto ctxt = cmp.getContext(); + auto kind = cmp.lhs()->getType().cast().getFKind(); + auto ty = convertType(fir::RealType::get(ctxt, kind)); + auto loc = cmp.getLoc(); + auto pos0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctxt); + L::SmallVector rp{ + rewriter.create(loc, ty, operands[0], pos0), + rewriter.create(loc, ty, operands[1], pos0)}; + auto rcp = rewriter.create(loc, ty, rp, cmp.getAttrs()); + auto pos1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctxt); + L::SmallVector ip{ + rewriter.create(loc, ty, operands[0], pos1), + rewriter.create(loc, ty, operands[1], pos1)}; + auto icp = rewriter.create(loc, ty, ip, cmp.getAttrs()); + L::SmallVector cp{rcp, icp}; + switch (cmp.getPredicate()) { + case fir::CmpFPredicate::OEQ: // .EQ. + rewriter.replaceOpWithNewOp(cmp, ty, cp); + break; + case fir::CmpFPredicate::UNE: // .NE. + rewriter.replaceOpWithNewOp(cmp, ty, cp); + break; + default: + rewriter.replaceOp(cmp, rcp.getResult()); + break; + } + return matchSuccess(); + } +}; + +struct CmpfOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto cmp = M::cast(op); + auto type = convertType(cmp.getType()); + rewriter.replaceOpWithNewOp(cmp, type, operands, + cmp.getAttrs()); + return matchSuccess(); + } +}; + +/// convert value of from-type to value of to-type struct ConvertOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -635,10 +810,10 @@ struct ConvertOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto convert = M::cast(op); - auto fromTy_ = lowering.convertType(convert.value()->getType()); - auto fromTy = lowering.unwrap(fromTy_); - auto toTy_ = lowering.convertType(convert.res()->getType()); - auto toTy = lowering.unwrap(toTy_); + auto fromTy_ = convertType(convert.value()->getType()); + auto fromTy = unwrap(fromTy_); + auto toTy_ = convertType(convert.res()->getType()); + auto toTy = unwrap(toTy_); auto *fromLLVMTy = fromTy.getUnderlyingType(); auto *toLLVMTy = toTy.getUnderlyingType(); auto *op0 = operands[0]; @@ -652,7 +827,7 @@ struct ConvertOpConversion : public FIROpConversion { if (toLLVMTy->isFloatingPointTy()) { unsigned fromBits = fromLLVMTy->getPrimitiveSizeInBits(); unsigned toBits = toLLVMTy->getPrimitiveSizeInBits(); - // TODO: what if different reps (F16, BF16) are the same size? + // FIXME: what if different reps (F16, BF16) are the same size? assert(fromBits != toBits); if (fromBits > toBits) v = rewriter.create(loc, toTy, op0); @@ -690,29 +865,7 @@ struct ConvertOpConversion : public FIROpConversion { } }; -// convert to reference to a reference to a subobject -struct CoordinateOpConversion : public FIROpConversion { - using FIROpConversion::FIROpConversion; - - M::PatternMatchResult - matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { - auto coor = M::cast(op); - auto baseOp = coor.ref()->getDefiningOp(); - auto loc = coor.getLoc(); - if (auto box = M::dyn_cast(baseOp)) { - // FIXME: for now assume this is always an array - M::Value *v = rewriter.create( - loc, lowering.convertType(coor.getType()), box.memref(), operands); - rewriter.replaceOp(op, v); - return matchSuccess(); - } - TODO(coor); - return matchSuccess(); - } -}; - -// virtual call to a method in a dispatch table +/// virtual call to a method in a dispatch table struct DispatchOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -720,7 +873,7 @@ struct DispatchOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto dispatch = M::cast(op); - auto ty = lowering.convertType(dispatch.getFunctionType()); + auto ty = convertType(dispatch.getFunctionType()); // get the table, lookup the method, fetch the func-ptr rewriter.replaceOpWithNewOp(dispatch, ty, operands); TODO(dispatch); @@ -728,7 +881,7 @@ struct DispatchOpConversion : public FIROpConversion { } }; -// dispatch table for a Fortran derived type +/// dispatch table for a Fortran derived type struct DispatchTableOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -741,7 +894,7 @@ struct DispatchTableOpConversion : public FIROpConversion { } }; -// entry in a dispatch table; binds a method-name to a function +/// entry in a dispatch table; binds a method-name to a function struct DTEntryOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -754,7 +907,7 @@ struct DTEntryOpConversion : public FIROpConversion { } }; -// create a CHARACTER box +/// create a CHARACTER box struct EmboxCharOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -766,7 +919,7 @@ struct EmboxCharOpConversion : public FIROpConversion { auto b = operands[1]; auto loc = emboxchar.getLoc(); auto ctx = emboxchar.getContext(); - auto ty = lowering.convertType(emboxchar.getType()); + auto ty = convertType(emboxchar.getType()); auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); auto un = rewriter.create(loc, ty); @@ -777,7 +930,7 @@ struct EmboxCharOpConversion : public FIROpConversion { } }; -// create a generic box on a memory reference +/// create a generic box on a memory reference struct EmboxOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -785,12 +938,67 @@ struct EmboxOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto embox = M::cast(op); - TODO(embox); + auto loc = embox.getLoc(); + auto dialect = getDialect(); + auto ty = unwrap(convertType(embox.getType())); + auto alloca = genAllocaWithType(loc, ty, 24, defaultAlign, rewriter); + auto c0 = genConstantOffset(loc, rewriter, 0); + auto rty = unwrap(operands[0]->getType()).getPointerTo(); + auto f0p = genGEP(loc, rty, rewriter, alloca, c0, c0); + auto f0p_ = rewriter.create(loc, rty, f0p); + rewriter.create(loc, operands[0], f0p_); + auto i64Ty = M::LLVM::LLVMType::getInt64Ty(dialect); + auto i64PtrTy = i64Ty.getPointerTo(); + auto f1p = genGEPToField(loc, i64PtrTy, rewriter, alloca, c0, 1); + auto c0_ = rewriter.create(loc, i64Ty, c0); + rewriter.create(loc, c0_, f1p); + auto i32PtrTy = M::LLVM::LLVMType::getInt32Ty(dialect).getPointerTo(); + auto f2p = genGEPToField(loc, i32PtrTy, rewriter, alloca, c0, 2); + rewriter.create(loc, c0, f2p); + auto i8Ty = M::LLVM::LLVMType::getInt8Ty(dialect); + auto i8PtrTy = M::LLVM::LLVMType::getInt8PtrTy(dialect); + auto c0__ = rewriter.create(loc, i8Ty, c0); + auto f3p = genGEPToField(loc, i8PtrTy, rewriter, alloca, c0, 3); + rewriter.create(loc, c0__, f3p); + auto f4p = genGEPToField(loc, i8PtrTy, rewriter, alloca, c0, 4); + rewriter.create(loc, c0__, f4p); + auto f5p = genGEPToField(loc, i8PtrTy, rewriter, alloca, c0, 5); + rewriter.create(loc, c0__, f5p); + auto f6p = genGEPToField(loc, i8PtrTy, rewriter, alloca, c0, 6); + rewriter.create(loc, c0__, f6p); + // FIXME: copy the dims info, etc. + + rewriter.replaceOp(embox, alloca.getResult()); return matchSuccess(); } + + /// Generate an alloca of size `size` and cast it to type `toTy` + M::LLVM::BitcastOp + genAllocaWithType(M::Location loc, M::LLVM::LLVMType toTy, unsigned size, + unsigned alignment, + M::ConversionPatternRewriter &rewriter) const { + auto i8Ty = M::LLVM::LLVMType::getInt8PtrTy(getDialect()); + auto thisPt = rewriter.saveInsertionPoint(); + auto *thisBlock = rewriter.getInsertionBlock(); + auto func = M::cast(thisBlock->getParentOp()); + rewriter.setInsertionPointToStart(&func.front()); + auto size_ = genConstantOffset(loc, rewriter, size); + auto al = rewriter.create(loc, i8Ty, size_, alignment); + rewriter.restoreInsertionPoint(thisPt); + return rewriter.create(loc, toTy, al); + } + + M::LLVM::BitcastOp genGEPToField(M::Location loc, M::LLVM::LLVMType ty, + M::ConversionPatternRewriter &rewriter, + M::Value *base, M::Value *zero, + int field) const { + auto coff = genConstantOffset(loc, rewriter, field); + auto gep = genGEP(loc, ty, rewriter, base, zero, coff); + return rewriter.create(loc, ty, gep); + } }; -// create a procedure pointer box +/// create a procedure pointer box struct EmboxProcOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -802,7 +1010,7 @@ struct EmboxProcOpConversion : public FIROpConversion { auto b = operands[1]; auto loc = emboxproc.getLoc(); auto ctx = emboxproc.getContext(); - auto ty = lowering.convertType(emboxproc.getType()); + auto ty = convertType(emboxproc.getType()); auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); auto un = rewriter.create(loc, ty); @@ -813,7 +1021,34 @@ struct EmboxProcOpConversion : public FIROpConversion { } }; -// extract a subobject value from an ssa-value of aggregate type +/// return true if all `Value`s in `operands` are `ConstantOp`s +bool allConstants(OperandTy operands) { + for (auto *opnd : operands) { + if (auto defop = opnd->getDefiningOp()) + if (dyn_cast(defop) || + dyn_cast(defop)) + continue; + return false; + } + return true; +} + +M::Attribute getValue(M::Value *value) { + assert(value->getDefiningOp()); + if (auto v = dyn_cast(value->getDefiningOp())) + return v.value(); + if (auto v = dyn_cast(value->getDefiningOp())) + return v.value(); + assert(false && "must be a constant op"); + return {}; +} + +template +inline void appendTo(L::SmallVectorImpl &dest, L::ArrayRef from) { + dest.append(from.begin(), from.end()); +} + +/// Extract a subobject value from an ssa-value of aggregate type struct ExtractValueOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -821,73 +1056,231 @@ struct ExtractValueOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto extractVal = M::cast(op); - TODO(extractVal); + auto ty = convertType(extractVal.getType()); + assert(allConstants(operands.drop_front(1))); + // since all indices are constants use LLVM's extractvalue instruction + L::SmallVector attrs; + for (int i = 1, end = operands.size(); i < end; ++i) + attrs.push_back(getValue(operands[i])); + auto position = M::ArrayAttr::get(attrs, extractVal.getContext()); + rewriter.replaceOpWithNewOp(extractVal, ty, + operands[0], position); return matchSuccess(); } }; -// Compute the offset of a field in a variable of derived type. A value of type -// field can only be used as an argument to a coordinate_of, extract_value, or -// insert_value operation. It derives it's meaning from the context of where it -// is used. -struct FieldIndexOpConversion : public FIROpConversion { +/// InsertValue is the generalized instruction for the composition of new +/// aggregate type values. +struct InsertValueOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { - auto fieldindex = M::cast(op); - rewriter.replaceOp(fieldindex, {}); + auto insertVal = cast(op); + auto ty = convertType(insertVal.getType()); + assert(allConstants(operands.drop_front(2))); + // since all indices must be constants use LLVM's insertvalue instruction + L::SmallVector attrs; + for (int i = 2, end = operands.size(); i < end; ++i) + attrs.push_back(getValue(operands[i])); + auto position = M::ArrayAttr::get(attrs, insertVal.getContext()); + rewriter.replaceOpWithNewOp( + insertVal, ty, operands[0], operands[1], position); return matchSuccess(); } }; -// Replace the fir-end op with a null -struct FirEndOpConversion : public FIROpConversion { +/// return true if all `Value`s in `operands` are not `FieldIndexOp`s +bool noFieldIndexOps(M::Operation::operand_range operands) { + for (auto *opnd : operands) { + if (auto defop = opnd->getDefiningOp()) + if (dyn_cast(defop)) + return false; + } + return true; +} + +/// convert to reference to a reference to a subobject +struct CoordinateOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { - rewriter.replaceOp(op, {}); + auto coor = M::cast(op); + auto ty = convertType(coor.getType()); + auto loc = coor.getLoc(); + M::Value *base = operands[0]; + auto c0 = genConstantIndex(loc, lowering.indexType(), rewriter, 0); + + // The base can be a boxed reference or a raw reference + if (auto boxTy = coor.ref()->getType().dyn_cast()) { + if (coor.getNumOperands() == 2) { + auto *coorPtr = *coor.coor().begin(); + auto *s = coorPtr->getDefiningOp(); + if (dyn_cast_or_null(s)) { + auto *lenParam = operands[1]; // byte offset + auto bc = rewriter.create(loc, voidPtrTy(), base); + auto uty = unwrap(ty); + auto gep = genGEP(loc, uty, rewriter, bc, lenParam); + rewriter.replaceOpWithNewOp(coor, uty, gep); + return matchSuccess(); + } + } + auto c0_ = genConstantOffset(loc, rewriter, 0); + auto pty = unwrap(convertType(boxTy.getEleTy())).getPointerTo(); + // Extract the boxed reference + auto p = genGEP(loc, pty, rewriter, base, c0, c0_); + base = rewriter.create(loc, pty, p); + } + + L::SmallVector offs{c0}; + auto indices = operands.drop_front(1); + offs.append(indices.begin(), indices.end()); + if (noFieldIndexOps(coor.coor())) { + // do not need to lower any field index ops, so use a GEP + rewriter.replaceOpWithNewOp(coor, ty, base, offs); + return matchSuccess(); + } + + // lower the field index ops by walking the indices + auto bty = coor.ref()->getType().cast(); + M::Type baseTy = ReferenceType::get(bty.getEleTy()); + L::SmallVector args{c0}; + args.append(coor.coor().begin(), coor.coor().end()); + + M::Value *retval = base; + assert(offs.size() == args.size() && "must have same arity"); + unsigned pos = 0; + for (unsigned i = 0, sz = offs.size(); i != sz; ++i) { + assert(pos <= i); + if (auto *defop = args[i]->getDefiningOp()) + if (auto field = dyn_cast(defop)) { + auto memTy = unwrap(convertType(baseTy)).getPointerTo(); + M::Value *gep = retval; + if (i - pos > 0) + gep = genGEP(loc, memTy, rewriter, gep, arguments(offs, pos, i)); + auto bc = rewriter.create(loc, voidPtrTy(), gep); + auto gep_ = genGEP(loc, voidPtrTy(), rewriter, bc, offs[i]); + pos = i + 1; + baseTy = baseTy.cast().getType(field.field_id()); + retval = rewriter.create(loc, convertType(baseTy), + gep_); + continue; + } + if (auto ptrTy = baseTy.dyn_cast()) { + baseTy = ptrTy.getEleTy(); + } else if (auto ptrTy = baseTy.dyn_cast()) { + baseTy = ptrTy.getEleTy(); + } else if (auto ptrTy = baseTy.dyn_cast()) { + baseTy = ptrTy.getEleTy(); + } else if (auto arrTy = baseTy.dyn_cast()) { + // FIXME: unchecked advance over array dims + i += arrTy.getDimension() - 1; + baseTy = arrTy.getEleTy(); + } else if (auto strTy = baseTy.dyn_cast()) { + baseTy = strTy.getType(getIntValue(offs[i])); + } else if (auto strTy = baseTy.dyn_cast()) { + baseTy = strTy.getType(getIntValue(offs[i])); + } else { + assert(false && "unhandled type"); + } + } + if (pos < offs.size()) + retval = genGEP(loc, unwrap(ty), rewriter, retval, + arguments(offs, pos, offs.size())); + rewriter.replaceOp(coor, retval); return matchSuccess(); } + + L::SmallVector arguments(L::ArrayRef vec, + unsigned s, unsigned e) const { + return {vec.begin() + s, vec.begin() + e}; + } + + int64_t getIntValue(M::Value *val) const { + if (val) + if (auto *defop = val->getDefiningOp()) + if (auto constOp = dyn_cast(defop)) + return constOp.getValue(); + assert(false && "must be a constant"); + return 0; + } }; -// call free function -struct FreeMemOpConversion : public FIROpConversion { +/// convert a field index to a runtime function that computes the byte offset of +/// the dynamic field +struct FieldIndexOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; - M::LLVM::LLVMType getVoidPtrType() const { - return M::LLVM::LLVMType::getInt8PtrTy(getDialect()); + // NB: most field references should be resolved by this point + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto field = M::cast(op); + // call the compiler generated function to determine the byte offset of + // the field at runtime + auto symAttr = M::SymbolRefAttr::get(methodName(field), field.getContext()); + L::SmallVector attrs{ + rewriter.getNamedAttr("callee", symAttr)}; + auto ty = lowering.offsetType(); + rewriter.replaceOpWithNewOp(field, ty, operands, attrs); + return matchSuccess(); } - M::FuncOp genFreeFunc(M::Operation *op, - M::ConversionPatternRewriter &rewriter) const { - M::FuncOp freeFunc = - op->getParentOfType().lookupSymbol("free"); - if (!freeFunc) { - auto freeType = rewriter.getFunctionType(getVoidPtrType(), {}); - freeFunc = M::FuncOp::create(rewriter.getUnknownLoc(), "free", freeType); - op->getParentOfType().push_back(freeFunc); + // constructing the name of the method + inline static std::string methodName(FieldIndexOp field) { + L::Twine fldName = field.field_id(); + // note: using std::string to dodge a bug in g++ 7.4.0 + std::string tyName = field.on_type().cast().getName(); + L::Twine methodName = "_QQOFFSETOF_" + tyName + "_" + fldName; + return methodName.str(); + } +}; + +struct LenParamIndexOpConversion + : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + // FIXME: this should be specialized by the runtime target + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto lenp = M::cast(op); + auto ity = lowering.indexType(); + auto onty = lenp.getOnType(); + // size of portable descriptor + const unsigned boxsize = 24; // FIXME + unsigned offset = boxsize; + // add the size of the rows of triples + if (auto arr = onty.dyn_cast()) { + offset += 3 * arr.getDimension(); } - return freeFunc; + // advance over some addendum fields + const unsigned addendumOffset = sizeof(void *) + sizeof(uint64_t); + offset += addendumOffset; + // add the offset into the LENs + offset += 0; // FIXME + auto attr = rewriter.getI64IntegerAttr(offset); + rewriter.replaceOpWithNewOp(lenp, ity, attr); + return matchSuccess(); } +}; + +/// lower the fir.end operation to a null (erasing it) +struct FirEndOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; M::PatternMatchResult matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { - auto freemem = M::cast(op); - auto freeFunc = genFreeFunc(freemem, rewriter); - M::Value *casted = rewriter.create( - freemem.getLoc(), getVoidPtrType(), operands[0]); - auto sym = rewriter.getSymbolRefAttr(freeFunc); - rewriter.replaceOpWithNewOp( - freemem, llvm::ArrayRef(), sym, casted); + rewriter.replaceOp(op, {}); return matchSuccess(); } }; +/// lower a gendims operation into a sequence of writes to a temp struct GenDimsOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -896,9 +1289,35 @@ struct GenDimsOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto gendims = M::cast(op); - TODO(gendims); + auto loc = gendims.getLoc(); + auto ty = convertType(gendims.getType()); + auto ptrTy = unwrap(ty).getPointerTo(); + auto alloca = genAlloca(loc, ptrTy, defaultAlign, rewriter); + unsigned offIndex = 0; + auto c0 = genConstantOffset(loc, rewriter, 0); + auto ipty = lowering.indexType().getPointerTo(); + for (auto op : operands) { + auto offset = genConstantOffset(loc, rewriter, offIndex); + auto gep = genGEP(loc, ipty, rewriter, alloca, c0, offset); + rewriter.create(loc, op, gep); + } + rewriter.replaceOpWithNewOp(gendims, ptrTy, alloca); return matchSuccess(); } + + // Generate an alloca of size `size` and cast it to type `toTy` + M::LLVM::AllocaOp genAlloca(M::Location loc, M::LLVM::LLVMType toTy, + unsigned alignment, + M::ConversionPatternRewriter &rewriter) const { + auto thisPt = rewriter.saveInsertionPoint(); + auto *thisBlock = rewriter.getInsertionBlock(); + auto func = M::cast(thisBlock->getParentOp()); + rewriter.setInsertionPointToStart(&func.front()); + auto size = genConstantOffset(loc, rewriter, 1); + auto rv = rewriter.create(loc, toTy, size, alignment); + rewriter.restoreInsertionPoint(thisPt); + return rv; + } }; struct GenTypeDescOpConversion : public FIROpConversion { @@ -908,7 +1327,9 @@ struct GenTypeDescOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto gentypedesc = M::cast(op); - TODO(gentypedesc); + auto ty = unwrap(convertType(gentypedesc.getInType())).getPointerTo(); + std::string name = "fixme"; // FIXME: get the mangled name + rewriter.replaceOpWithNewOp(gentypedesc, ty, name); return matchSuccess(); } }; @@ -932,9 +1353,9 @@ struct GlobalOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto global = M::cast(op); - auto tyAttr = M::TypeAttr::get(lowering.convertType(global.getType())); + auto tyAttr = M::TypeAttr::get(convertType(global.getType())); M::UnitAttr isConst; - if (global.getAttrOfType("constant").getValue()) + if (global.getAttr("constant")) isConst = M::UnitAttr::get(global.getContext()); auto name = M::StringAttr::get( global.getAttrOfType(M::SymbolTable::getSymbolAttrName()) @@ -949,39 +1370,6 @@ struct GlobalOpConversion : public FIROpConversion { } }; -// InsertValue is the generalized instruction for the composition of new -// aggregate type values. -struct InsertValueOpConversion : public FIROpConversion { - using FIROpConversion::FIROpConversion; - - M::PatternMatchResult - matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { - auto insertVal = cast(op); - TODO(insertVal); - // rewriter.replaceOpWithNewOp(insertVal, ...); - return matchSuccess(); - } -}; - -// Compute the index of the LEN param in the descriptor addendum. A value of -// type field can only be used as an argument to a coordinate_of, extract_value, -// or insert_value operation. It derives it's meaning from the context of where -// it is used. A LEN parameter cannot be an aggregate itself and thus a -// LenParamIndexOp can appear only once and must be last in the argument list. -struct LenParamIndexOpConversion - : public FIROpConversion { - using FIROpConversion::FIROpConversion; - - M::PatternMatchResult - matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { - auto lenparam = M::cast(op); - rewriter.replaceOp(lenparam, {}); - return matchSuccess(); - } -}; - // convert to LLVM IR dialect `load` struct LoadOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -990,7 +1378,7 @@ struct LoadOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto load = M::cast(op); - auto ty = lowering.convertType(load.getType()); + auto ty = convertType(load.getType()); auto at = load.getAttrs(); rewriter.replaceOpWithNewOp(op, ty, operands, at); return matchSuccess(); @@ -1010,7 +1398,7 @@ struct LoopOpConversion : public FIROpConversion { } }; -// TODO: how do we want to enforce this in LLVM-IR? +// FIXME: how do we want to enforce this in LLVM-IR? struct NoReassocOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -1024,6 +1412,22 @@ struct NoReassocOpConversion : public FIROpConversion { } }; +void genCaseLadderStep(M::Location loc, M::Value *cmp, M::Block *dest, + OperandTy destOps, + M::ConversionPatternRewriter &rewriter) { + auto *thisBlock = rewriter.getInsertionBlock(); + auto *newBlock = rewriter.createBlock(dest); + rewriter.setInsertionPointToEnd(thisBlock); + L::SmallVector dest_{dest, newBlock}; + L::SmallVector, 2> destOps_{destOps, {}}; + rewriter.create(loc, L::ArrayRef{cmp}, dest_, + destOps_); + rewriter.setInsertionPointToEnd(newBlock); +} + +/// Conversion of `fir.select_case` +/// +/// TODO: lowering of CHARACTER type cases struct SelectCaseOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -1033,12 +1437,106 @@ struct SelectCaseOpConversion : public FIROpConversion { L::ArrayRef destOperands, M::ConversionPatternRewriter &rewriter) const override { auto selectcase = M::cast(op); - TODO(selectcase); + auto conds = selectcase.getNumConditions(); + auto attrName = SelectCaseOp::AttrName; + auto caseAttr = selectcase.getAttrOfType(attrName); + auto cases = caseAttr.getValue(); + // Type can be CHARACTER, INTEGER, or LOGICAL (C1145) + auto ty = selectcase.getSelector()->getType(); + (void)ty; + auto &selector = operands[0]; + unsigned nextOp = 1; + auto loc = selectcase.getLoc(); + assert(conds > 0 && "selectcase must have cases"); + for (unsigned t = 0; t != conds; ++t) { + auto &attr = cases[t]; + if (attr.dyn_cast_or_null()) { + auto cmp = rewriter.create( + loc, M::LLVM::ICmpPredicate::eq, selector, operands[nextOp++]); + genCaseLadderStep(loc, cmp, destinations[t], destOperands[t], rewriter); + continue; + } + if (attr.dyn_cast_or_null()) { + auto cmp = rewriter.create( + loc, M::LLVM::ICmpPredicate::sle, operands[nextOp++], selector); + genCaseLadderStep(loc, cmp, destinations[t], destOperands[t], rewriter); + continue; + } + if (attr.dyn_cast_or_null()) { + auto cmp = rewriter.create( + loc, M::LLVM::ICmpPredicate::sle, selector, operands[nextOp++]); + genCaseLadderStep(loc, cmp, destinations[t], destOperands[t], rewriter); + continue; + } + if (attr.dyn_cast_or_null()) { + auto cmp = rewriter.create( + loc, M::LLVM::ICmpPredicate::sle, operands[nextOp++], selector); + auto *thisBlock = rewriter.getInsertionBlock(); + auto *newBlock1 = rewriter.createBlock(destinations[t]); + auto *newBlock2 = rewriter.createBlock(destinations[t]); + rewriter.setInsertionPointToEnd(thisBlock); + L::SmallVector dests{newBlock1, newBlock2}; + L::SmallVector, 2> destOps{{}, {}}; + rewriter.create(loc, L::ArrayRef{cmp}, + dests, destOps); + rewriter.setInsertionPointToEnd(newBlock1); + auto cmp2 = rewriter.create( + loc, M::LLVM::ICmpPredicate::sle, selector, operands[nextOp++]); + L::SmallVector dest2{destinations[t], newBlock2}; + L::SmallVector, 2> destOp2{destOperands[t], {}}; + rewriter.create(loc, L::ArrayRef{cmp2}, + dest2, destOp2); + rewriter.setInsertionPointToEnd(newBlock2); + continue; + } + assert(attr.dyn_cast_or_null()); + assert((t + 1 == conds) && "unit must be last"); + L::SmallVector empty; + rewriter.replaceOpWithNewOp( + selectcase, empty, destinations[t], destOperands[t]); + } return matchSuccess(); } }; -// conversion of fir::SelectOp +template +void selectMatchAndRewrite(FIRToLLVMTypeConverter &lowering, M::Operation *op, + OperandTy operands, + L::ArrayRef destinations, + L::ArrayRef destOperands, + M::ConversionPatternRewriter &rewriter) { + auto select = M::cast(op); + + // We could target the LLVM switch instruction, but it isn't part of the + // LLVM IR dialect. Create an if-then-else ladder instead. + auto conds = select.getNumConditions(); + auto attrName = OP::AttrName; + auto caseAttr = select.template getAttrOfType(attrName); + auto cases = caseAttr.getValue(); + auto ty = select.getSelector()->getType(); + auto ity = lowering.convertType(ty); + auto &selector = operands[0]; + auto loc = select.getLoc(); + assert(conds > 0 && "select must have cases"); + for (unsigned t = 0; t != conds; ++t) { + auto &attr = cases[t]; + if (auto intAttr = attr.template dyn_cast_or_null()) { + auto ci = rewriter.create( + loc, ity, rewriter.getIntegerAttr(ty, intAttr.getInt())); + auto cmp = rewriter.create( + loc, M::LLVM::ICmpPredicate::eq, selector, ci); + genCaseLadderStep(loc, cmp, destinations[t], destOperands[t], rewriter); + continue; + } + assert(attr.template dyn_cast_or_null()); + assert((t + 1 == conds) && "unit must be last"); + L::SmallVector empty; + rewriter.replaceOpWithNewOp(select, empty, destinations[t], + destOperands[t]); + } +} + +/// conversion of fir::SelectOp to an if-then-else ladder struct SelectOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -1047,12 +1545,13 @@ struct SelectOpConversion : public FIROpConversion { L::ArrayRef destinations, L::ArrayRef destOperands, M::ConversionPatternRewriter &rewriter) const override { - auto select = M::cast(op); - TODO(select); + selectMatchAndRewrite(lowering, op, operands, destinations, + destOperands, rewriter); return matchSuccess(); } }; +/// conversion of fir::SelectRankOp to an if-then-else ladder struct SelectRankOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -1061,8 +1560,8 @@ struct SelectRankOpConversion : public FIROpConversion { L::ArrayRef destinations, L::ArrayRef destOperands, M::ConversionPatternRewriter &rewriter) const override { - auto selectrank = M::cast(op); - TODO(selectrank); + selectMatchAndRewrite( + lowering, op, operands, destinations, destOperands, rewriter); return matchSuccess(); } }; @@ -1076,7 +1575,7 @@ struct SelectTypeOpConversion : public FIROpConversion { L::ArrayRef destinations, L::ArrayRef destOperands, M::ConversionPatternRewriter &rewriter) const override { - auto selecttype = M::cast(op); + auto selecttype = M::cast(op); TODO(selecttype); return matchSuccess(); } @@ -1146,8 +1645,8 @@ struct UndefOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto undef = M::cast(op); - rewriter.replaceOpWithNewOp( - undef, lowering.convertType(undef.getType())); + rewriter.replaceOpWithNewOp(undef, + convertType(undef.getType())); return matchSuccess(); } }; @@ -1181,29 +1680,11 @@ struct WhereOpConversion : public FIROpConversion { } }; -// Generate code for complex addition/subtraction -template -M::LLVM::InsertValueOp complexSum(OPTY sumop, OperandTy opnds, - M::ConversionPatternRewriter &rewriter, - FIRToLLVMTypeConverter &lowering) { - auto a = opnds[0]; - auto b = opnds[1]; - auto loc = sumop.getLoc(); - auto ctx = sumop.getContext(); - auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); - auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); - auto ty = lowering.convertType(sumop.getType()); - auto x = rewriter.create(loc, ty, a, c0); - auto x_ = rewriter.create(loc, ty, b, c0); - auto rx = rewriter.create(loc, ty, x, x_); - auto y = rewriter.create(loc, ty, a, c1); - auto y_ = rewriter.create(loc, ty, b, c1); - auto ry = rewriter.create(loc, ty, y, y_); - auto r = rewriter.create(loc, ty); - auto r_ = rewriter.create(loc, ty, r, rx, c0); - return rewriter.create(loc, ty, r_, ry, c1); -} +// +// Primitive operations on Real (floating-point) types +// +/// Convert a floating-point primitive template void lowerRealBinaryOp(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter, @@ -1269,6 +1750,46 @@ struct ModfOpConversion : public FIROpConversion { } }; +struct NegfOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto neg = M::cast(op); + auto ty = convertType(neg.getType()); + rewriter.replaceOpWithNewOp(neg, ty, operands); + return matchSuccess(); + } +}; + +// +// Primitive operations on Complex types +// + +/// Generate code for complex addition/subtraction +template +M::LLVM::InsertValueOp complexSum(OPTY sumop, OperandTy opnds, + M::ConversionPatternRewriter &rewriter, + FIRToLLVMTypeConverter &lowering) { + auto a = opnds[0]; + auto b = opnds[1]; + auto loc = sumop.getLoc(); + auto ctx = sumop.getContext(); + auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); + auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); + auto ty = lowering.convertType(sumop.getType()); + auto x = rewriter.create(loc, ty, a, c0); + auto x_ = rewriter.create(loc, ty, b, c0); + auto rx = rewriter.create(loc, ty, x, x_); + auto y = rewriter.create(loc, ty, a, c1); + auto y_ = rewriter.create(loc, ty, b, c1); + auto ry = rewriter.create(loc, ty, y, y_); + auto r = rewriter.create(loc, ty); + auto r_ = rewriter.create(loc, ty, r, rx, c0); + return rewriter.create(loc, ty, r_, ry, c1); +} + struct AddcOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -1299,6 +1820,7 @@ struct SubcOpConversion : public FIROpConversion { } }; +/// Inlined complex multiply struct MulcOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -1306,7 +1828,7 @@ struct MulcOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto mulc = M::cast(op); - // TODO: should this just call __muldc3 ? + // FIXME: should this just call __muldc3 ? // result: (xx'-yy')+i(xy'+yx') auto a = operands[0]; auto b = operands[1]; @@ -1314,7 +1836,7 @@ struct MulcOpConversion : public FIROpConversion { auto ctx = mulc.getContext(); auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); - auto ty = lowering.convertType(mulc.getType()); + auto ty = convertType(mulc.getType()); auto x = rewriter.create(loc, ty, a, c0); auto x_ = rewriter.create(loc, ty, b, c0); auto xx_ = rewriter.create(loc, ty, x, x_); @@ -1334,6 +1856,7 @@ struct MulcOpConversion : public FIROpConversion { } }; +/// Inlined complex division struct DivcOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -1341,7 +1864,7 @@ struct DivcOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto divc = M::cast(op); - // TODO: should this just call __divdc3 ? + // FIXME: should this just call __divdc3 ? // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' auto a = operands[0]; auto b = operands[1]; @@ -1349,7 +1872,7 @@ struct DivcOpConversion : public FIROpConversion { auto ctx = divc.getContext(); auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); - auto ty = lowering.convertType(divc.getType()); + auto ty = convertType(divc.getType()); auto x = rewriter.create(loc, ty, a, c0); auto x_ = rewriter.create(loc, ty, b, c0); auto xx_ = rewriter.create(loc, ty, x, x_); @@ -1374,41 +1897,40 @@ struct DivcOpConversion : public FIROpConversion { } }; +struct NegcOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + M::ConversionPatternRewriter &rewriter) const override { + auto neg = M::cast(op); + auto ctxt = neg.getContext(); + auto ty = convertType(neg.getType()); + auto loc = neg.getLoc(); + auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctxt); + auto &o0 = operands[0]; + auto rp = rewriter.create(loc, ty, o0, c0); + auto nrp = rewriter.create(loc, ty, rp); + auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctxt); + auto ip = rewriter.create(loc, ty, o0, c1); + auto nip = rewriter.create(loc, ty, ip); + auto r = rewriter.create(loc, ty, o0, nrp, c0); + rewriter.replaceOpWithNewOp(neg, ty, r, nip, c1); + return matchSuccess(); + } +}; + // Lower a SELECT operation into a cascade of conditional branches. The last // case must be the `true` condition. -inline void rewriteSelectConstruct(M::Operation *op, OperandTy operands, - L::ArrayRef dests, - L::ArrayRef destOperands, - M::OpBuilder &rewriter) { - L::SmallVector noargs; - L::SmallVector blocks; - auto loc{op->getLoc()}; - blocks.push_back(rewriter.getInsertionBlock()); - for (std::size_t i = 1; i < dests.size(); ++i) - blocks.push_back(rewriter.createBlock(dests[0])); - rewriter.setInsertionPointToEnd(blocks[0]); - if (dests.size() == 1) { - rewriter.create(loc, dests[0], destOperands[0]); - return; - } - rewriter.create(loc, operands[1], dests[0], destOperands[0], - blocks[1], noargs); - for (std::size_t i = 1; i < dests.size() - 1; ++i) { - rewriter.setInsertionPointToEnd(blocks[i]); - rewriter.create(loc, operands[i + 1], dests[i], - destOperands[i], blocks[i + 1], noargs); - } - std::size_t last{dests.size() - 1}; - rewriter.setInsertionPointToEnd(blocks[last]); - rewriter.create(loc, dests[last], destOperands[last]); -} - /// Convert FIR dialect to LLVM dialect /// /// This pass lowers all FIR dialect operations to LLVM IR dialect. An /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. struct FIRToLLVMLoweringPass : public M::ModulePass { void runOnModule() override { + if (ClDisableFirToLLVMIR) + return; + auto &context{getContext()}; FIRToLLVMTypeConverter typeConverter{&context}; M::OwningRewritePatternList patterns; @@ -1418,29 +1940,30 @@ struct FIRToLLVMLoweringPass : public M::ModulePass { BoxCharLenOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxProcHostOpConversion, BoxRankOpConversion, BoxTypeDescOpConversion, - CallOpConversion, ConvertOpConversion, CoordinateOpConversion, + CallOpConversion, CmpcOpConversion, CmpfOpConversion, + ConstantOpConversion, ConvertOpConversion, CoordinateOpConversion, DispatchOpConversion, DispatchTableOpConversion, DivcOpConversion, DivfOpConversion, DTEntryOpConversion, EmboxCharOpConversion, - EmboxOpConversion, EmboxProcOpConversion, FirEndOpConversion, - ExtractValueOpConversion, FieldIndexOpConversion, FreeMemOpConversion, + EmboxOpConversion, EmboxProcOpConversion, FieldIndexOpConversion, + FirEndOpConversion, ExtractValueOpConversion, FreeMemOpConversion, GenDimsOpConversion, GenTypeDescOpConversion, GlobalEntryOpConversion, GlobalOpConversion, InsertValueOpConversion, LenParamIndexOpConversion, LoadOpConversion, LoopOpConversion, ModfOpConversion, MulcOpConversion, - MulfOpConversion, NoReassocOpConversion, SelectCaseOpConversion, - SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion, - StoreOpConversion, SubcOpConversion, SubfOpConversion, - UnboxCharOpConversion, UnboxOpConversion, UnboxProcOpConversion, - UndefOpConversion, UnreachableOpConversion, WhereOpConversion>( - &context, typeConverter); + MulfOpConversion, NegcOpConversion, NegfOpConversion, + NoReassocOpConversion, SelectCaseOpConversion, SelectOpConversion, + SelectRankOpConversion, SelectTypeOpConversion, StoreOpConversion, + SubcOpConversion, SubfOpConversion, UnboxCharOpConversion, + UnboxOpConversion, UnboxProcOpConversion, UndefOpConversion, + UnreachableOpConversion, WhereOpConversion>(&context, typeConverter); M::populateStdToLLVMConversionPatterns(typeConverter, patterns); M::ConversionTarget target{context}; target.addLegalDialect(); // required NOP stubs for applying a full conversion target.addDynamicallyLegalOp( - [&](M::ModuleOp op) { return true; }); + [&](M::ModuleOp) { return true; }); target.addDynamicallyLegalOp( - [&](M::ModuleTerminatorOp op) { return true; }); + [&](M::ModuleTerminatorOp) { return true; }); // apply the patterns if (M::failed(M::applyFullConversion( @@ -1455,16 +1978,32 @@ struct FIRToLLVMLoweringPass : public M::ModulePass { /// Lower from LLVM IR dialect to proper LLVM-IR and dump the module struct LLVMIRLoweringPass : public M::ModulePass { void runOnModule() override { + if (ClDisableLLVM) + return; + + genDispatchTableMap(); + if (auto llvmModule{M::translateModuleToLLVMIR(getModule())}) { std::error_code ec; - L::raw_fd_ostream stream("a.ll", ec, L::sys::fs::F_None); + L::raw_fd_ostream stream(outputName, ec, L::sys::fs::F_None); stream << *llvmModule << '\n'; - } else { - auto ctxt{getModule().getContext()}; - M::emitError(M::UnknownLoc::get(ctxt), "could not emit LLVM-IR\n"); - signalPassFailure(); + L::errs() << outputName << " written\n"; + return; } + + auto ctxt{getModule().getContext()}; + M::emitError(M::UnknownLoc::get(ctxt), "could not emit LLVM-IR\n"); + signalPassFailure(); } + + void setOutputName(L::StringRef output) { outputName = output; } + +private: + void genDispatchTableMap() { + // TODO + } + + L::StringRef outputName; }; } // namespace @@ -1473,6 +2012,8 @@ std::unique_ptr fir::createFIRToLLVMPass() { return std::make_unique(); } -std::unique_ptr fir::createLLVMDialectToLLVMPass() { - return std::make_unique(); +std::unique_ptr fir::createLLVMDialectToLLVMPass(L::StringRef output) { + auto pass = std::make_unique(); + pass->setOutputName(output); + return pass; } diff --git a/lib/fir/Transforms/CSE.cpp b/lib/fir/Transforms/CSE.cpp index 3092f6a61eca..2e765dfedb46 100644 --- a/lib/fir/Transforms/CSE.cpp +++ b/lib/fir/Transforms/CSE.cpp @@ -44,6 +44,10 @@ static llvm::cl::opt ClLeaveEffects("keep-effects", llvm::cl::desc("disable cleaning up effects attributes"), llvm::cl::init(false), llvm::cl::Hidden); +static llvm::cl::opt ClDisableCSE("disable-cse", + llvm::cl::desc("disable CSE pass"), + llvm::cl::init(false), + llvm::cl::Hidden); namespace { @@ -146,7 +150,7 @@ struct BasicCSE : public FunctionPass { void cleanupBlock(Block *bb) { for (auto &inst : *bb) { - if (fir::nonVolatileLoad(&inst)) { + if (fir::nonVolatileLoad(&inst) || fir::pureCall(&inst)) { inst.removeAttr(Identifier::get("effects_token", inst.getContext())); } else if (inst.getNumRegions()) { for (auto ®ion : inst.getRegions()) @@ -175,7 +179,7 @@ LogicalResult BasicCSE::simplifyOperation(ScopedMapTy &knownValues, if (op->getNumRegions() != 0) return failure(); - if (!op->hasNoSideEffect() && !fir::nonVolatileLoad(op)) + if (!op->hasNoSideEffect() && !fir::nonVolatileLoad(op) && !fir::pureCall(op)) return failure(); // If the operation is already trivially dead just add it to the erase list. @@ -210,7 +214,7 @@ void BasicCSE::simplifyBlock(ScopedMapTy &knownValues, DominanceInfo &domInfo, Block *bb) { std::intptr_t token = reinterpret_cast(bb); for (auto &inst : *bb) { - if (fir::nonVolatileLoad(&inst)) + if (fir::nonVolatileLoad(&inst) || fir::pureCall(&inst)) inst.setAttr("effects_token", IntegerAttr::get(IndexType::get(inst.getContext()), token)); if (dyn_cast(&inst) || fir::impureCall(&inst)) @@ -285,6 +289,9 @@ void BasicCSE::simplifyRegion(ScopedMapTy &knownValues, DominanceInfo &domInfo, } void BasicCSE::runOnFunction() { + if (ClDisableCSE) + return; + /// A scoped hash table of defining operations within a function. { ScopedMapTy knownValues; diff --git a/lib/fir/Transforms/MemToReg.cpp b/lib/fir/Transforms/MemToReg.cpp index 2104f8ddf97c..f94198fd7674 100644 --- a/lib/fir/Transforms/MemToReg.cpp +++ b/lib/fir/Transforms/MemToReg.cpp @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "fir/Analysis/IteratedDominanceFrontier.h" -#include "fir/Dialect.h" +#include "fir/FIRDialect.h" #include "fir/FIROps.h" #include "fir/Transforms/Passes.h" #include "mlir/Analysis/Dominance.h" @@ -18,12 +18,18 @@ #include #include +namespace L = llvm; namespace M = mlir; using namespace fir; using DominatorTree = M::DominanceInfo; +static L::cl::opt + ClDisableMemToReg("disable-mem2reg", + L::cl::desc("disable memory to register pass"), + L::cl::init(false), L::cl::Hidden); + /// A generalized version of a mem-to-reg pass suitable for use with an MLIR /// dialect. This code was ported from the LLVM project. MLIR differs with its /// use of block arguments rather than PHI nodes, etc. @@ -49,8 +55,8 @@ bool isAllocaPromotable(ALLOCA &ae) { template struct AllocaInfo { - llvm::SmallVector definingBlocks; - llvm::SmallVector usingBlocks; + L::SmallVector definingBlocks; + L::SmallVector usingBlocks; M::Operation *onlyStore; M::Block *onlyBlock; @@ -103,9 +109,10 @@ struct RenamePassData { RenamePassData(M::Block *b, M::Block *p, const ValVector &v) : BB(b), Pred(p), Values(v) {} + RenamePassData(RenamePassData &&) = default; + RenamePassData &operator=(RenamePassData &&) = delete; RenamePassData(const RenamePassData &) = delete; RenamePassData &operator=(const RenamePassData &) = delete; - RenamePassData(RenamePassData &&) = default; ~RenamePassData() = default; M::Block *BB; @@ -115,7 +122,7 @@ struct RenamePassData { template struct LargeBlockInfo { - using INMap = llvm::DenseMap; + using INMap = L::DenseMap; INMap instNumbers; static bool isInterestingInstruction(M::Operation &I) { @@ -169,16 +176,16 @@ struct MemToReg : public M::FunctionPass> { /// Contains a stable numbering of basic blocks to avoid non-determinstic /// behavior. - llvm::DenseMap BBNumbers; + L::DenseMap BBNumbers; /// Reverse mapping of Allocas. - llvm::DenseMap allocaLookup; + L::DenseMap allocaLookup; /// The set of basic blocks the renamer has already visited. - llvm::SmallPtrSet Visited; + L::SmallPtrSet Visited; - llvm::DenseMap, unsigned> BlockArgs; - llvm::DenseMap, unsigned> argToAllocaMap; + L::DenseMap, unsigned> BlockArgs; + L::DenseMap, unsigned> argToAllocaMap; bool rewriteSingleStoreAlloca(ALLOCA &AI, AllocaInfo &Info, @@ -225,13 +232,13 @@ struct MemToReg : public M::FunctionPass> { } // Otherwise, we *can* safely rewrite this load. - M::Value *ReplVal = onlyStore.getOperand(0); + M::Value *replVal = onlyStore.getOperand(0); // If the replacement value is the load, this must occur in unreachable // code. - if (ReplVal == LI.getResult()) - ReplVal = builder->create(LI.getLoc(), LI.getType()); + if (replVal == LI.getResult()) + replVal = builder->create(LI.getLoc(), LI.getType()); - LI.replaceAllUsesWith(ReplVal); + LI.replaceAllUsesWith(replVal); LI.erase(); LBI.deleteValue(LI); } @@ -251,17 +258,19 @@ struct MemToReg : public M::FunctionPass> { AllocaInfo &Info, LargeBlockInfo &LBI) { // Walk the use-def list of the alloca, getting the locations of all stores. - using StoresByIndexTy = llvm::SmallVector, 64>; - StoresByIndexTy StoresByIndex; + using StoresByIndexTy = + L::SmallVector, 64>; + StoresByIndexTy storesByIndex; for (auto U = AI.getResult()->use_begin(), E = AI.getResult()->use_end(); U != E; U++) if (STORE SI = M::dyn_cast(U->getOwner())) - StoresByIndex.emplace_back(LBI.getInstructionIndex(SI), &SI); + storesByIndex.emplace_back(LBI.getInstructionIndex(SI), + SI.getOperation()); // Sort the stores by their index, making it efficient to do a lookup with a // binary search. - llvm::sort(StoresByIndex, llvm::less_first()); + L::sort(storesByIndex, L::less_first()); // Walk all of the loads from this alloca, replacing them with the nearest // store above them, if any. @@ -275,11 +284,12 @@ struct MemToReg : public M::FunctionPass> { unsigned LoadIdx = LBI.getInstructionIndex(LI); // Find the nearest store that has a lower index than this load. - typename StoresByIndexTy::iterator I = llvm::lower_bound( - StoresByIndex, std::make_pair(LoadIdx, static_cast(nullptr)), - llvm::less_first()); - if (I == StoresByIndex.begin()) { - if (StoresByIndex.empty()) { + typename StoresByIndexTy::iterator I = L::lower_bound( + storesByIndex, + std::make_pair(LoadIdx, static_cast(nullptr)), + L::less_first()); + if (I == storesByIndex.begin()) { + if (storesByIndex.empty()) { // If there are no stores, the load takes the undef value. auto undef = builder->create(LI.getLoc(), LI.getType()); LI.replaceAllUsesWith(undef.getResult()); @@ -292,14 +302,14 @@ struct MemToReg : public M::FunctionPass> { // Otherwise, there was a store before this load, the load takes its // value. Note, if the load was marked as nonnull we don't want to lose // that information when we erase it. So we preserve it with an assume. - M::Value *ReplVal = std::prev(I)->second->getOperand(0); + M::Value *replVal = std::prev(I)->second->getOperand(0); // If the replacement value is the load, this must occur in unreachable // code. - if (ReplVal == LI) - ReplVal = builder->create(LI.getLoc(), LI.getType()); + if (replVal == LI) + replVal = builder->create(LI.getLoc(), LI.getType()); - LI.replaceAllUsesWith(ReplVal); + LI.replaceAllUsesWith(replVal); } LI.erase(); @@ -310,9 +320,9 @@ struct MemToReg : public M::FunctionPass> { while (!AI.use_empty()) { auto *ae = AI.getResult(); for (auto ai = ae->user_begin(), E = ae->user_end(); ai != E; ai++) - if (STORE SI = M::dyn_cast(*ai)) { - SI.erase(); - LBI.deleteValue(SI); + if (STORE si = M::dyn_cast(*ai)) { + si.erase(); + LBI.deleteValue(si); } } @@ -321,14 +331,14 @@ struct MemToReg : public M::FunctionPass> { } void computeLiveInBlocks(ALLOCA &ae, AllocaInfo &Info, - const llvm::SmallPtrSetImpl &DefBlocks, - llvm::SmallPtrSetImpl &liveInBlks) { + const L::SmallPtrSetImpl &DefBlocks, + L::SmallPtrSetImpl &liveInBlks) { auto *AI = ae.getOperation(); // To determine liveness, we must iterate through the predecessors of blocks // where the def is live. Blocks are added to the worklist if we need to // check their predecessors. Start with all the using blocks. - llvm::SmallVector LiveInBlockWorklist( - Info.usingBlocks.begin(), Info.usingBlocks.end()); + L::SmallVector LiveInBlockWorklist(Info.usingBlocks.begin(), + Info.usingBlocks.end()); // If any of the using blocks is also a definition block, check to see if // the definition occurs before or after the use. If it happens before the @@ -535,39 +545,34 @@ struct MemToReg : public M::FunctionPass> { if (!srcOpn) continue; - auto Src = M::dyn_cast(srcOpn); - if (!Src) + if (!M::dyn_cast(srcOpn)) continue; - llvm::DenseMap::iterator AI = + L::DenseMap::iterator ai = allocaLookup.find(srcOpn); - if (AI == allocaLookup.end()) + if (ai == allocaLookup.end()) continue; - M::Value *V = IncomingVals[AI->second]; - // Anything using the load now uses the current value. - LI.replaceAllUsesWith(V); + LI.replaceAllUsesWith(IncomingVals[ai->second]); LI.erase(); } else if (auto SI = M::dyn_cast(opn)) { auto *dstOpn = SI.getOperand(1)->getDefiningOp(); if (!dstOpn) continue; - // Delete this instruction and mark the name as the current holder of - // the value - auto Dest = M::dyn_cast(dstOpn); - if (!Dest) + if (!M::dyn_cast(dstOpn)) continue; - llvm::DenseMap::iterator ai = + L::DenseMap::iterator ai = allocaLookup.find(dstOpn); if (ai == allocaLookup.end()) continue; - // what value were we writing? - unsigned AllocaNo = ai->second; - addValue(IncomingVals, AllocaNo, SI.getOperand(0)); + // Delete this instruction and mark the name as the current holder of + // the value + unsigned allocaNo = ai->second; + addValue(IncomingVals, allocaNo, SI.getOperand(0)); SI.erase(); } } @@ -579,7 +584,7 @@ struct MemToReg : public M::FunctionPass> { return; // Keep track of the successors so we don't visit the same successor twice - llvm::SmallPtrSet VisitedSuccs; + L::SmallPtrSet VisitedSuccs; // Handle the first successor without using the worklist. VisitedSuccs.insert(*I); @@ -622,9 +627,9 @@ struct MemToReg : public M::FunctionPass> { // If we haven't computed a numbering for the BB's in the function, do // so now. if (BBNumbers.empty()) { - unsigned ID = 0; + unsigned id = 0; for (auto &BB : F) - BBNumbers[&BB] = ID++; + BBNumbers[&BB] = id++; } // Keep the reverse mapping of the 'Allocas' array for the rename pass. @@ -636,27 +641,27 @@ struct MemToReg : public M::FunctionPass> { // insertion of dead phi nodes. // Unique the set of defining blocks for efficient lookup. - llvm::SmallPtrSet DefBlocks(info.definingBlocks.begin(), - info.definingBlocks.end()); + L::SmallPtrSet defBlocks(info.definingBlocks.begin(), + info.definingBlocks.end()); // Determine which blocks the value is live in. These are blocks which // lead to uses. - llvm::SmallPtrSet liveInBlks; - computeLiveInBlocks(ae, info, DefBlocks, liveInBlks); + L::SmallPtrSet liveInBlks; + computeLiveInBlocks(ae, info, defBlocks, liveInBlks); // At this point, we're committed to promoting the alloca using IDF's, // and the standard SSA construction algorithm. Determine which blocks // need phi nodes and see if we can optimize out some work by avoiding // insertion of dead phi nodes. IDF.setLiveInBlocks(liveInBlks); - IDF.setDefiningBlocks(DefBlocks); - llvm::SmallVector PHIBlocks; - IDF.calculate(PHIBlocks); - llvm::sort(PHIBlocks, [this](M::Block *A, M::Block *B) { + IDF.setDefiningBlocks(defBlocks); + L::SmallVector phiBlocks; + IDF.calculate(phiBlocks); + L::sort(phiBlocks, [this](M::Block *A, M::Block *B) { return BBNumbers.find(A)->second < BBNumbers.find(B)->second; }); - for (M::Block *BB : PHIBlocks) + for (M::Block *BB : phiBlocks) addBlockArgument(BB, ae, allocaNum); aes.push_back(ae); @@ -671,20 +676,20 @@ struct MemToReg : public M::FunctionPass> { // Set the incoming values for the basic block to be null values for all // of the alloca's. We do this in case there is a load of a value that // has not been stored yet. In this case, it will get this null value. - RenamePassData::ValVector Values(allocas.size()); + RenamePassData::ValVector values(allocas.size()); for (unsigned i = 0, e = allocas.size(); i != e; ++i) - Values[i] = builder->create(allocas[i].getLoc(), + values[i] = builder->create(allocas[i].getLoc(), allocas[i].getAllocatedType()); // Walks all basic blocks in the function performing the SSA rename // algorithm and inserting the phi nodes we marked as necessary std::vector renameWorklist; - renameWorklist.emplace_back(&F.front(), nullptr, Values); + renameWorklist.emplace_back(&F.front(), nullptr, values); do { - RenamePassData RPD(std::move(renameWorklist.back())); + RenamePassData rpd(std::move(renameWorklist.back())); renameWorklist.pop_back(); // renamePass may add new worklist entries. - renamePass(RPD.BB, RPD.Pred, RPD.Values, renameWorklist); + renamePass(rpd.BB, rpd.Pred, rpd.Values, renameWorklist); } while (!renameWorklist.empty()); // The renamer uses the Visited set to avoid infinite loops. Clear it @@ -707,6 +712,9 @@ struct MemToReg : public M::FunctionPass> { /// run the MemToReg pass on the FIR dialect void runOnFunction() override { + if (ClDisableMemToReg) + return; + auto f = this->getFunction(); auto &entry = f.front(); auto bldr = M::OpBuilder(f.getBody()); diff --git a/test/fir/aggregate.fir b/test/fir/aggregate.fir new file mode 100644 index 000000000000..c306ec117230 --- /dev/null +++ b/test/fir/aggregate.fir @@ -0,0 +1,11 @@ +func @f_tuple(%a : tuple) -> i64 { + %0 = constant 1 : i32 + %1 = fir.extract_value %a, %0 : (tuple, i32) -> i64 + return %1 : i64 +} + +func @f_record(%a : !fir.type) -> i64 { + %0 = fir.field_index("fb") + %1 = fir.extract_value %a, %0 : (!fir.type, !fir.field) -> i64 + return %1 : i64 +} diff --git a/test/fir/alloc.fir b/test/fir/alloc.fir new file mode 100644 index 000000000000..a1081c123a44 --- /dev/null +++ b/test/fir/alloc.fir @@ -0,0 +1,21 @@ +func @f1() -> !fir.ref { + %1 = fir.alloca i32 : !fir.ref + return %1 : !fir.ref +} + +func @f2() -> !fir.ref { + %0 = constant 100 : index + %1 = fir.alloca i32, %0 : !fir.ref + return %1 : !fir.ref +} + +func @f3() -> !fir.heap { + %1 = fir.allocmem i32 : !fir.heap + return %1 : !fir.heap +} + +func @f4() -> !fir.heap { + %0 = constant 100 : index + %1 = fir.allocmem i32, %0 : !fir.heap + return %1 : !fir.heap +} diff --git a/test/fir/bugs/bug0001.fir b/test/fir/bugs/bug0001.fir new file mode 100644 index 000000000000..5535adb6d0e3 --- /dev/null +++ b/test/fir/bugs/bug0001.fir @@ -0,0 +1,41 @@ +func @r_incr(%arg0: !fir.ref) -> f64 { + %0 = fir.alloca f64 {name = "r_incr"} : !fir.ref + %1 = fir.load %arg0 : !fir.ref + fir.store %1 to %0 : !fir.ref + %2 = fir.load %arg0 : !fir.ref + %cst = constant 1.000000e+00 : f64 + %3 = fir.addf %2, %cst : f64 + fir.store %3 to %arg0 : !fir.ref + %4 = fir.load %0 : !fir.ref + return %4 : f64 +} + +func @_MAIN() { + %0 = fir.alloca f64 {name = "y"} : !fir.ref + %1 = fir.alloca f64 {name = "y2"} : !fir.ref + %2 = fir.alloca f64 {name = "y1"} : !fir.ref + %3 = fir.alloca f64 {name = "x"} : !fir.ref + %cst = constant 1.000000e+00 : f64 + fir.store %cst to %3 : !fir.ref + %4 = fir.load %3 : !fir.ref + fir.store %4 to %2 : !fir.ref + %5 = fir.load %2 : !fir.ref + %6 = call @r_incr(%3) : (!fir.ref) -> f64 + %7 = fir.addf %5, %6 : f64 + fir.store %7 to %1 : !fir.ref + %8 = fir.load %1 : !fir.ref + %9 = fir.load %3 : !fir.ref + %10 = fir.addf %8, %9 : f64 + fir.store %10 to %0 : !fir.ref + %c1_i32 = constant 1 : i32 + %11 = call @__F18IOa_BeginExternalListOutput(%c1_i32) : (i32) -> !fir.ref + %12 = fir.load %0 : !fir.ref + call @__F18IOa_OutputReal64(%11, %12) : (!fir.ref, f64) -> () + call @__F18IOa_EndIOStatement(%11) : (!fir.ref) -> () + return +} + +func @__F18IOa_BeginExternalListOutput(i32) -> !fir.ref +func @__F18IOa_OutputInteger64(!fir.ref, i64) +func @__F18IOa_OutputReal64(!fir.ref, f64) +func @__F18IOa_EndIOStatement(!fir.ref) diff --git a/test/fir/bugs/bug0002.fir b/test/fir/bugs/bug0002.fir new file mode 100644 index 000000000000..f163e76035e9 --- /dev/null +++ b/test/fir/bugs/bug0002.fir @@ -0,0 +1,12 @@ +func @foo() -> f32 { + %0 = fir.alloca f32 {name = "foo"} : !fir.ref + %1 = fir.alloca f32 {name = "x"} : !fir.ref + %cst = constant 4.200000e+01 : f32 + fir.store %cst to %1 : !fir.ref + %cst_0 = constant 6.600000e+01 : f32 + fir.store %cst_0 to %1 : !fir.ref + %2 = fir.load %1 : !fir.ref + fir.store %2 to %0 : !fir.ref + %3 = fir.load %0 : !fir.ref + return %3 : f32 +} diff --git a/test/fir/compare.fir b/test/fir/compare.fir new file mode 100644 index 000000000000..8aa9f6864dc1 --- /dev/null +++ b/test/fir/compare.fir @@ -0,0 +1,29 @@ +func @cmp(%a : !fir.real<10>, %b : !fir.real<10>) -> i1 { + %1 = "fir.cmpf"(%a, %b) {predicate = 1} : (!fir.real<10>, !fir.real<10>) -> i1 + return %1 : i1 +} + +func @cmp2(%a : !fir.real<16>, %b : !fir.real<16>) -> i1 { + %1 = fir.cmpf "ult", %a, %b : !fir.real<16> + return %1 : i1 +} + +func @cmp3(%a : !fir.complex<4>, %b : !fir.complex<4>) -> i1 { + %1 = fir.cmpc "ueq", %a, %b : !fir.complex<4> + return %1 : i1 +} + +func @neg1(%a : !fir.real<8>) -> !fir.real<8> { + %1 = "fir.negf"(%a) : (!fir.real<8>) -> !fir.real<8> + return %1 : !fir.real<8> +} + +func @neg2(%a : !fir.real<8>) -> !fir.real<8> { + %1 = fir.negf %a : !fir.real<8> + return %1 : !fir.real<8> +} + +func @neg3(%a : !fir.complex<8>) -> !fir.complex<8> { + %1 = fir.negc %a : !fir.complex<8> + return %1 : !fir.complex<8> +} diff --git a/test/fir/complex.fir b/test/fir/complex.fir index 9f69e41bbd2b..958baa430ff4 100644 --- a/test/fir/complex.fir +++ b/test/fir/complex.fir @@ -5,3 +5,18 @@ func @foo(%a : !fir.complex<4>, %b : !fir.complex<4>, %c : !fir.complex<4>, %d : %4 = fir.divc %3, %e : !fir.complex<4> return %4 : !fir.complex<4> } + +func @f2(%a : !fir.complex<4>) -> f32 { + %0 = constant 0 : i32 + %1 = fir.extract_value %a, %0 : (!fir.complex<4>, i32) -> f32 + return %1 : f32 +} + +func @f3(%a : !fir.complex<4>) -> !fir.complex<4> { + %0 = constant 1 : i32 + %1 = fir.extract_value %a, %0 : (!fir.complex<4>, i32) -> f32 + %2 = constant 0.0 : f32 + %3 = fir.subf %2, %1 : f32 + %4 = fir.insert_value %a, %3, %0 : (!fir.complex<4>, f32, i32) -> !fir.complex<4> + return %4 : !fir.complex<4> +} diff --git a/test/fir/constant.fir b/test/fir/constant.fir new file mode 100644 index 000000000000..67ca40f36611 --- /dev/null +++ b/test/fir/constant.fir @@ -0,0 +1,19 @@ +func @x() -> !fir.array<3x!fir.char<1>> { + %1 = fir.constant "xyz" : !fir.array<3x!fir.char<1>> + return %1 : !fir.array<3x!fir.char<1>> +} + +func @y() -> !fir.real<10> { + %1 = fir.constant "42.4" : !fir.real<10> + return %1 : !fir.real<10> +} + +func @z() -> !fir.logical<1> { + %1 = fir.constant "true" : !fir.logical<1> + return %1 : !fir.logical<1> +} + +func @z2() -> !fir.ref> { + %1 = fir.constant "abc" : !fir.ref> + return %1 : !fir.ref> +} diff --git a/test/fir/dynlayout.fir b/test/fir/dynlayout.fir new file mode 100644 index 000000000000..5646db53975e --- /dev/null +++ b/test/fir/dynlayout.fir @@ -0,0 +1,38 @@ +// dynamic case + +// dynamically sized type +func @_QQSIZEOF_a(%p1 : i64, %p2 : i64) -> i64 { + %c1 = constant 1 : i64 // sizeof CHARACTER(1) + %1 = muli %p1, %c1 : i64 + %c2 = constant 2 : i64 // sizeof CHARACTER(2) + %2 = muli %p2, %c2 : i64 + %3 = addi %1, %2 : i64 + return %3 : i64 +} + +func @_QQOFFSETOF_a_f1(%p1 : i64, %p2 : i64) -> i32 { + %0 = constant 0 : i64 + %1 = fir.convert %0 : (i64) -> i32 + return %1 : i32 +} + +func @_QQOFFSETOF_a_f2(%p1 : i64, %p2 : i64) -> i32 { + %c1 = constant 1 : i64 + %1 = muli %p1, %c1 : i64 + %2 = fir.convert %1 : (i64) -> i32 + return %2 : i32 +} + +// get 'a%f2(x)' +func @f(%a : !fir.box>, f2:!fir.array>}>>, %x : i32, %q1 : i64, %q2 : i64) -> !fir.char<2> { + %p1p = fir.len_param_index p1, !fir.type + %p2p = fir.len_param_index p2, !fir.type + %p1c = fir.coordinate_of %a, %p1p : (!fir.box>, !fir.len) -> !fir.ref + %p2c = fir.coordinate_of %a, %p2p : (!fir.box>, !fir.len) -> !fir.ref + %p1 = fir.load %p1c : !fir.ref + %p2 = fir.load %p2c : !fir.ref + %1 = fir.field_index f2, !fir.type(%p1, %p2) : i64, i64 + %2 = fir.coordinate_of %a, %1, %x : (!fir.box>, !fir.field, i32) -> !fir.ref> + %3 = fir.load %2 : !fir.ref> + return %3 : !fir.char<2> +} diff --git a/test/fir/embox.fir b/test/fir/embox.fir index e3750a3c8b55..801e7241b424 100644 --- a/test/fir/embox.fir +++ b/test/fir/embox.fir @@ -1,6 +1,6 @@ #x0 = (d0, d1) -> (d1, d0) -func @f(%arg : !fir.ref>) { - %1 = fir.embox %arg [#x0] : (!fir.ref>) -> !fir.box, > +func @f(%arg : !fir.ref>) { + %1 = fir.embox %arg [#x0] : (!fir.ref>) -> !fir.box, > return } diff --git a/test/fir/fir-ops.fir b/test/fir/fir-ops.fir index a8c8fed7cca1..03eea8102aab 100644 --- a/test/fir/fir-ops.fir +++ b/test/fir/fir-ops.fir @@ -11,11 +11,11 @@ func @dvd2() -> !fir.type func @dvd3() -> !fir.type func @dvd4() -> !fir.type -func @arr1() -> !fir.array<10:f32> -func @arr2() -> !fir.array<10x10:f32> -func @arr3() -> !fir.array -func @arr4() -> !fir.array<10x?:f32> -func @arr5() -> !fir.array +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 @mem1() -> !fir.ref @@ -23,7 +23,7 @@ func @mem2() -> !fir.ptr func @mem3() -> !fir.heap func @mem4() -> !fir.ref<() -> ()> -func @box1() -> !fir.box> +func @box1() -> !fir.box> func @box2() -> !fir.boxchar<2> func @box3() -> !fir.boxproc<(i32, i32) -> i64> func @box4() -> !fir.box> @@ -45,39 +45,39 @@ func @nop() func @get_func() -> (() -> ()) func @instructions() { - %0 = fir.alloca !fir.array<10:i32> : !fir.ref> - %1 = fir.load %0 : !fir.ref> + %0 = fir.alloca !fir.array<10xi32> : !fir.ref> + %1 = fir.load %0 : !fir.ref> %2 = fir.alloca i32 : !fir.ref %3 = constant 22 : i32 fir.store %3 to %2 : !fir.ref %4 = fir.undefined i32 - %5 = fir.allocmem !fir.array<100:f32> : !fir.heap> - %6 = fir.embox %5 : (!fir.heap>) -> !fir.box> - %7 = fir.box_addr %6 : (!fir.box>) -> !fir.ref> + %5 = fir.allocmem !fir.array<100xf32> : !fir.heap> + %6 = fir.embox %5 : (!fir.heap>) -> !fir.box> + %7 = fir.box_addr %6 : (!fir.box>) -> !fir.ref> %c0 = constant 0 : index - %d1:3 = fir.box_dims %6, %c0 : (!fir.box>, index) -> (index, index, index) + %d1:3 = fir.box_dims %6, %c0 : (!fir.box>, index) -> (index, index, index) fir.call @print_index3(%d1#0, %d1#1, %d1#2) : (index, index, index) -> () %8 = fir.call @it1() : () -> !fir.int<4> - %9 = fir.box_elesize %6 : (!fir.box>) -> i64 - %10 = fir.box_isalloc %6 : (!fir.box>) -> i1 - %11 = fir.box_isarray %6 : (!fir.box>) -> i1 - %12 = fir.box_isptr %6 : (!fir.box>) -> i1 - %13 = fir.box_rank %6 : (!fir.box>) -> i64 - %14 = fir.box_tdesc %6 : (!fir.box>) -> !fir.tdesc> + %9 = fir.box_elesize %6 : (!fir.box>) -> i64 + %10 = fir.box_isalloc %6 : (!fir.box>) -> i1 + %11 = fir.box_isarray %6 : (!fir.box>) -> i1 + %12 = fir.box_isptr %6 : (!fir.box>) -> i1 + %13 = fir.box_rank %6 : (!fir.box>) -> i64 + %14 = fir.box_tdesc %6 : (!fir.box>) -> !fir.tdesc> %15 = fir.call @box2() : () -> !fir.boxchar<2> %16 = fir.boxchar_len %15 : (!fir.boxchar<2>) -> i32 %17 = fir.call @box3() : () -> !fir.boxproc<(i32, i32) -> i64> %18 = fir.boxproc_host %17 : (!fir.boxproc<(i32, i32) -> i64>) -> !fir.ref %19 = constant 10 : i32 - %20 = fir.coordinate_of %5, %19 : (!fir.heap>, i32) -> !fir.ref - %21 = fir.field_index("f") : !fir.field + %20 = fir.coordinate_of %5, %19 : (!fir.heap>, i32) -> !fir.ref + %21 = fir.field_index f, !fir.type %22 = fir.undefined !fir.type %23 = fir.extract_value %22, %21 : (!fir.type, !fir.field) -> f32 %c1 = constant 1 : i32 %24 = fir.gendims %c1, %19, %c1 : (i32, i32, i32) -> !fir.dims<1> %cf1 = constant 1.0 : f32 %25 = fir.insert_value %22, %cf1, %21 : (!fir.type, f32, !fir.field) -> !fir.type - %26 = fir.len_param_index("f") : !fir.field + %26 = fir.len_param_index f, !fir.type %27 = fir.call @box4() : () -> !fir.box> %28 = fir.dispatch "method"(%27) : (!fir.box>) -> i32 %29 = fir.convert %28 : (i32) -> i64 @@ -85,7 +85,7 @@ func @instructions() { fir.call @user_tdesc(%30) : (!fir.tdesc>) -> () %31 = fir.no_reassoc %29 : i64 fir.call @user_i64(%31) : (i64) -> () - fir.freemem %5 : !fir.heap> + fir.freemem %5 : !fir.heap> %32 = fir.call @get_func() : () -> (() -> ()) fir.call %32() : () -> () %33 = fir.address_of (@it1) : !fir.ref<() -> !fir.int<4>> diff --git a/test/fir/fir-types.fir b/test/fir/fir-types.fir index c61b7d98bde3..7cfcfc6363a7 100644 --- a/test/fir/fir-types.fir +++ b/test/fir/fir-types.fir @@ -13,20 +13,20 @@ func @dvd4() -> !fir.type func @dvd5() -> !fir.type func @dvd6() -> !fir.type>}> -func @arr1() -> !fir.array<10:f32> -func @arr2() -> !fir.array<10x10:f32> -func @arr3() -> !fir.array -func @arr4() -> !fir.array<10x?:f32> -func @arr5() -> !fir.array +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?x4x5x6x7x8x9:f32> +func @arr7() -> !fir.array<1x2x?x4x5x6x7x8x9xf32> func @mem1() -> !fir.ref func @mem2() -> !fir.ptr func @mem3() -> !fir.heap func @mem4() -> !fir.ref<() -> ()> -func @box1() -> !fir.box> +func @box1() -> !fir.box> func @box2() -> !fir.boxchar<2> func @box3() -> !fir.boxproc<(i32, i32) -> i64> func @box4() -> !fir.box @@ -34,5 +34,5 @@ func @box4() -> !fir.box func @oth1() -> !fir.dims<1> func @oth2() -> !fir.field -func @oth3() -> !fir.tdesc> +func @oth3() -> !fir.tdesc> func @oth4() -> !fir.dims<15> diff --git a/test/fir/loads.fir b/test/fir/loads.fir new file mode 100644 index 000000000000..2b05e7372633 --- /dev/null +++ b/test/fir/loads.fir @@ -0,0 +1,35 @@ +func @fun(%a : !fir.ref) -> i64 { + %1 = fir.load %a : !fir.ref + %2 = fir.load %a : !fir.ref + %3 = addi %1, %2 : i64 + %4 = fir.load %a : !fir.ref + %5 = addi %3, %4 : i64 + %6 = fir.load %a : !fir.ref + %7 = addi %5, %6 : i64 + %8 = fir.load %a : !fir.ref + %9 = addi %7, %8 : i64 + %10 = fir.load %a : !fir.ref + %11 = addi %10, %9 : i64 + %12 = fir.load %a : !fir.ref + %13 = addi %11, %12 : i64 + return %13 : i64 +} + +func @bar(%a : !fir.ref) -> i64 + +func @fun2(%a : !fir.ref) -> i64 { + %1 = fir.load %a : !fir.ref + %2 = fir.call @bar(%a) { pure = true } : (!fir.ref) -> i64 + %3 = addi %1, %2 : i64 + %4 = fir.call @bar(%a) { pure = true } : (!fir.ref) -> i64 + %5 = addi %3, %4 : i64 + %6 = fir.call @bar(%a) { pure = true } : (!fir.ref) -> i64 + %7 = addi %5, %6 : i64 + %8 = fir.call @bar(%a) { pure = true } : (!fir.ref) -> i64 + %9 = addi %7, %8 : i64 + %10 = fir.call @bar(%a) { pure = true } : (!fir.ref) -> i64 + %11 = addi %10, %9 : i64 + %12 = fir.call @bar(%a) { pure = true } : (!fir.ref) -> i64 + %13 = addi %11, %12 : i64 + return %13 : i64 +} diff --git a/test/fir/select.fir b/test/fir/select.fir new file mode 100644 index 000000000000..026d97b5a9f5 --- /dev/null +++ b/test/fir/select.fir @@ -0,0 +1,45 @@ +func @f(%a : i32) -> i32 { + %1 = constant 1 : i32 + %2 = constant 42 : i32 + fir.select %a : i32 [1, ^bb2(%1:i32), unit, ^bb3(%2:i32)] +^bb2(%3 : i32) : + return %3 : i32 +^bb3(%4 : i32) : + %5 = addi %4, %4 : i32 + return %5 : i32 +} + +func @g(%a : i32) -> i32 { + %1 = constant 1 : i32 + %2 = constant 42 : i32 + fir.select_rank %a : i32 [1, ^bb2(%1:i32), -1, ^bb4, unit, ^bb3(%2:i32)] +^bb2(%3 : i32) : + return %3 : i32 +^bb3(%4 : i32) : + %5 = addi %4, %4 : i32 + return %5 : i32 +^bb4: + return %a : i32 +} + +func @h(%a : i32) -> i32 { + %1 = constant 1 : i32 + %2 = constant 42 : i32 + %b1 = constant 4 : i32 + %b2 = constant 14 : i32 + %b3 = constant 82 : i32 + %b4 = constant 96 : i32 + fir.select_case %a : i32 [#fir.point, %1, ^bb2(%1:i32), #fir.lower, %b1, ^bb4, #fir.upper, %b2, ^bb6, #fir.interval, %b3, %b4, ^bb5, unit, ^bb3(%2:i32)] +^bb2(%3 : i32) : + return %3 : i32 +^bb3(%4 : i32) : + %5 = addi %4, %4 : i32 + br ^bb2(%5 : i32) +^bb4: + return %a : i32 +^bb5: + return %1 : i32 +^bb6: + %x = addi %b4, %b3 : i32 + return %x : i32 +} diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index ea5985098739..76a1c1ab9d3e 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -8,4 +8,5 @@ add_subdirectory(f18) -add_subdirectory(fml) +add_subdirectory(bbc) +add_subdirectory(tco) diff --git a/tools/fml/CMakeLists.txt b/tools/bbc/CMakeLists.txt similarity index 85% rename from tools/fml/CMakeLists.txt rename to tools/bbc/CMakeLists.txt index 1f79d37d0233..58e011fb18ed 100644 --- a/tools/fml/CMakeLists.txt +++ b/tools/bbc/CMakeLists.txt @@ -14,19 +14,21 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-string-conversion") -add_executable(fml - fml.cc +add_executable(bbc + bbc.cc ) set(LIBS + FIR + FIRTransforms MLIRAffineOps MLIRLLVMIR MLIRLoopOps MLIRStandardOps ) -whole_archive_link(fml ${LIBS}) -target_link_libraries(fml +whole_archive_link(bbc ${LIBS}) +target_link_libraries(bbc FortranCommon FortranParser FortranEvaluate @@ -36,4 +38,4 @@ target_link_libraries(fml ${LIBS} ) -install(TARGETS fml DESTINATION bin) +install(TARGETS bbc DESTINATION bin) diff --git a/tools/fml/fml.cc b/tools/bbc/bbc.cc similarity index 89% rename from tools/fml/fml.cc rename to tools/bbc/bbc.cc index 05293d6f54c1..1cc749a6d23b 100644 --- a/tools/fml/fml.cc +++ b/tools/bbc/bbc.cc @@ -14,7 +14,7 @@ // Temporary Fortran front end driver main program for development scaffolding. -#include "fir/Dialect.h" +#include "fir/FIRDialect.h" #include "fir/Tilikum/Tilikum.h" #include "fir/Transforms/Passes.h" #include "fir/Transforms/StdConverter.h" @@ -23,7 +23,7 @@ #include "../../lib/common/default-kinds.h" #include "../../lib/parser/characters.h" #include "../../lib/parser/dump-parse-tree.h" -#include "../../lib/parser/flang-features.h" +#include "../../lib/common/Fortran-features.h" #include "../../lib/parser/message.h" #include "../../lib/parser/parse-tree-visitor.h" #include "../../lib/parser/parse-tree.h" @@ -286,68 +286,15 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, pm.addPass(fir::createFIRToLLVMPass()); } if (driver.lowerToLLVMIR) { - pm.addPass(fir::createLLVMDialectToLLVMPass()); + pm.addPass(fir::createLLVMDialectToLLVMPass("a.ll")); } - auto result{pm.run(mlirModule)}; - if (driver.dumpFIR) { - llvm::errs() << ";== 2 ==\n"; - mlirModule.dump(); - } - return {}; -} - -// For handling .fir files (MLIR+FIR) -std::string CompileFir(std::string path, Fortran::parser::Options options, - DriverOptions &driver, - Fortran::semantics::SemanticsContext &semanticsContext) { - Br::instantiateBurnsideBridge(semanticsContext.defaultKinds()); - - // check that there is a file to load - llvm::ErrorOr> fileOrErr = - llvm::MemoryBuffer::getFileOrSTDIN(path); - if (std::error_code EC = fileOrErr.getError()) { - llvm::errs() << "Could not open file: " << EC.message() << '\n'; - std::exit(-1); - } - - // load the file into a module - llvm::SourceMgr sourceMgr; - sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), llvm::SMLoc()); - Br::getBridge().parseSourceFile(sourceMgr); - auto mlirModule = Br::getBridge().getModule(); - if (!Br::getBridge().validModule()) { - llvm::errs() << "Error can't load file " << path << '\n'; - std::exit(3); - } - if (mlir::failed(mlirModule.verify())) { - llvm::errs() << "Error verifying FIR module\n"; - std::exit(4); - } - - llvm::errs() << ";== 3 ==\n"; - mlirModule.dump(); - - // run passes - mlir::PassManager pm{mlirModule.getContext()}; - pm.addPass(fir::createMemToRegPass()); - pm.addPass(fir::createCSEPass()); - if (driver.lowerToStd) { - pm.addPass(fir::createFIRToStdPass()); - } - if (driver.lowerToLLVM) { - pm.addPass(fir::createFIRToLLVMPass()); - } - if (driver.lowerToLLVMIR) { - pm.addPass(fir::createLLVMDialectToLLVMPass()); - } - auto result{pm.run(mlirModule)}; - llvm::errs() << ";== 4 ==\n"; - mlirModule.dump(); - if (mlir::succeeded(result)) { - llvm::errs() << "a.ll written\n"; + if (mlir::succeeded(pm.run(mlirModule))) { + if (driver.dumpFIR) { + llvm::errs() << ";== 2 ==\n"; + mlirModule.dump(); + } } else { - llvm::errs() << "FAILED\n"; - std::exit(5); + llvm::errs() << "oops, pass manager reported failure\n"; } return {}; } @@ -407,8 +354,7 @@ int main(int argc, char *const argv[]) { Fortran::common::IntrinsicTypeDefaultKinds defaultKinds; - std::vector fortranSources, firSources, otherSources, - relocatables; + std::vector fortranSources, otherSources, relocatables; bool anyFiles{false}; while (!args.empty()) { @@ -428,8 +374,6 @@ int main(int argc, char *const argv[]) { suffix == "cuf" || suffix == "CUF" || suffix == "f18" || suffix == "F18" || suffix == "ff18") { fortranSources.push_back(arg); - } else if (suffix == "fir" || suffix == "ir" || suffix == "mlir") { - firSources.push_back(arg); } else if (suffix == "o" || suffix == "a") { relocatables.push_back(arg); } else { @@ -622,12 +566,6 @@ int main(int argc, char *const argv[]) { relocatables.push_back(relo); } } - for (const auto &path : firSources) { - std::string relo{CompileFir(path, options, driver, semanticsContext)}; - if (!driver.compileOnly && !relo.empty()) { - relocatables.push_back(relo); - } - } for (const auto &path : otherSources) { std::string relo{CompileOtherLanguage(path, driver)}; if (!driver.compileOnly && !relo.empty()) { diff --git a/tools/f18/CMakeLists.txt b/tools/f18/CMakeLists.txt index 7046f621aba4..c151f05033a0 100644 --- a/tools/f18/CMakeLists.txt +++ b/tools/f18/CMakeLists.txt @@ -54,14 +54,14 @@ foreach(filename ${MODULES}) set(depends ${include}/__fortran_builtins.mod) endif() add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/${filename}.mod - COMMAND f18 -fparse-only -fdebug-semantics ${PROJECT_SOURCE_DIR}/tools/flang/module/${filename}.f90 + COMMAND f18 -fparse-only -fdebug-semantics ${FLANG_SOURCE_DIR}/module/${filename}.f90 WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include" - DEPENDS f18 ${PROJECT_SOURCE_DIR}/tools/flang/module/${filename}.f90 + DEPENDS f18 ${FLANG_SOURCE_DIR}/module/${filename}.f90 ) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/${filename}.f18.mod - COMMAND f18 -fparse-only -fdebug-semantics -module-suffix .f18.mod ${PROJECT_SOURCE_DIR}/tools/flang/module/${filename}.f90 + COMMAND f18 -fparse-only -fdebug-semantics -module-suffix .f18.mod ${FLANG_SOURCE_DIR}/module/${filename}.f90 WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include" - DEPENDS f18 ${PROJECT_SOURCE_DIR}/tools/flang/module/${filename}.f90 + DEPENDS f18 ${FLANG_SOURCE_DIR}/module/${filename}.f90 ) list(APPEND MODULE_FILES ${include}/${filename}.mod) list(APPEND MODULE_FILES ${include}/${filename}.f18.mod) diff --git a/tools/tco/CMakeLists.txt b/tools/tco/CMakeLists.txt new file mode 100644 index 000000000000..3e2ae6844ea4 --- /dev/null +++ b/tools/tco/CMakeLists.txt @@ -0,0 +1,46 @@ +# Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(LIBS + FIR + FIRTransforms + MLIRAffineToStandard + MLIRAnalysis + MLIRLLVMIR + MLIRParser + MLIRPass + MLIRTransforms + MLIRSupport +) + +add_llvm_library(FirTcoLib tco.cpp) +target_link_libraries(FirTcoLib ${LIBS}) + +set(EXE_LIBS + ${LIBS} + MLIRAffineOps + MLIRLoopToStandard + MLIREDSC + MLIRLinalg + MLIRLoopOps + MLIRStandardOps + MLIRStandardToLLVM + MLIRVectorOps + MLIRVectorToLLVM +) + +add_llvm_tool(tco tco.cpp) +llvm_update_compile_flags(tco) +whole_archive_link(tco ${EXE_LIBS}) +target_link_libraries(tco PRIVATE FIR MLIRIR FirTcoLib ${EXE_LIBS} LLVMSupport) diff --git a/tools/tco/tco.cpp b/tools/tco/tco.cpp new file mode 100644 index 000000000000..ca68472abb78 --- /dev/null +++ b/tools/tco/tco.cpp @@ -0,0 +1,87 @@ +//===- 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 "fir/FIRDialect.h" +#include "fir/Tilikum/Tilikum.h" +#include "fir/Transforms/Passes.h" +#include "fir/Transforms/StdConverter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include "mlir/IR/MLIRContext.h" +#include "mlir/IR/Module.h" +#include "mlir/Parser.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Transforms/Passes.h" + +using namespace llvm; + +static cl::opt ClInput( + cl::Positional, cl::Required, cl::desc("")); + +static cl::opt ClOutput("o", cl::desc("Specify output filename"), + cl::value_desc("filename"), cl::init("a.ll")); + +// compile a .fir file +int compileFIR() { + // check that there is a file to load + ErrorOr> fileOrErr = + MemoryBuffer::getFileOrSTDIN(ClInput); + + 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 " << ClInput << '\n'; + return 2; + } + if (mlir::failed(owningRef->verify())) { + errs() << "Error verifying FIR module\n"; + return 4; + } + + errs() << ";== input ==\n"; + owningRef->dump(); + + // run passes + mlir::PassManager pm{context.get()}; + pm.addPass(fir::createMemToRegPass()); + pm.addPass(fir::createCSEPass()); + pm.addPass(fir::createFIRToStdPass()); + pm.addPass(fir::createFIRToLLVMPass()); + pm.addPass(fir::createLLVMDialectToLLVMPass(ClOutput)); + if (mlir::succeeded(pm.run(*owningRef))) { + errs() << ";== output ==\n"; + owningRef->dump(); + } else { + errs() << "FAILED: " << ClInput << '\n'; + return 8; + } + return 0; +} + +int main(int argc, char *const argv[]) { + cl::ParseCommandLineOptions(argc, argv, "Tilikum Crossing Opt\n"); + return compileFIR(); +} From 51f2c099f0a5da21ef90e9de6ec417a4047ab565 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Mon, 18 Nov 2019 09:13:36 -0800 Subject: [PATCH 030/123] revise build instructions to add help on build failure --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index b5e1b09224c2..86a3b8102432 100644 --- a/README.md +++ b/README.md @@ -54,3 +54,11 @@ One can, for example, do this with make as follows. ``` Or, of course, use their favorite build tool (such as ninja). + +### Problems + +Despite best efforts, there may be situations where the above repos will +get out of sync, and the build will fail. One may want to try using the +alternate MLIR fork at `git@github.com:schweitzpgi/mlir.git` instead of the +one at `git@github.com:flang-compiler/f18-mlir.git` (see step 1) and repeat +the above process. From 579bf42f98973268fdf1ffc5aee9d45cd900d6bd Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Mon, 18 Nov 2019 09:56:39 -0800 Subject: [PATCH 031/123] fix build failures --- lib/burnside/convert-expr.cc | 14 ++++++-------- lib/burnside/fe-helper.cc | 12 ++++++------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 62a47c794f3e..ea7cd324a53e 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -576,15 +576,13 @@ class ExprLowering { auto unwrapTy{arrTy.cast().getEleTy()}; auto seqTy{unwrapTy.cast()}; auto shape = seqTy.getShape(); - if (shape.hasValue()) { - if (dims < shape->size()) { - fir::SequenceType::Bounds newBnds; - // follow Fortran semantics and remove columns - for (unsigned i = 0; i < dims; ++i) { - newBnds.push_back((*shape)[i]); - } - return fir::SequenceType::get({newBnds}, seqTy.getEleTy()); + if ((shape.size() > 0) && (dims < shape.size())) { + fir::SequenceType::Shape newBnds; + // follow Fortran semantics and remove columns + for (unsigned i = 0; i < dims; ++i) { + newBnds.push_back(shape[i]); } + return fir::SequenceType::get(newBnds, seqTy.getEleTy()); } return seqTy.getEleTy(); } diff --git a/lib/burnside/fe-helper.cc b/lib/burnside/fe-helper.cc index eaa649afed19..0e5083be440a 100644 --- a/lib/burnside/fe-helper.cc +++ b/lib/burnside/fe-helper.cc @@ -222,7 +222,7 @@ class TypeBuilder { fir::SequenceType::Shape genSeqShape(Se::SymbolRef symbol) { assert(symbol->IsObjectArray()); - fir::SequenceType::Bounds bounds; + fir::SequenceType::Shape bounds; auto &details = symbol->get(); const auto size = details.shape().size(); for (auto &ss : details.shape()) { @@ -239,13 +239,13 @@ class TypeBuilder { bounds.emplace_back( toConstant(ubv.value()) - toConstant(lbv.value()) + 1); } else { - bounds.emplace_back(0); + bounds.emplace_back(-1); } } else { - bounds.emplace_back(0); + bounds.emplace_back(-1); } } - return {bounds}; + return bounds; } /// Type consing from a symbol. A symbol's type must be created from the type @@ -307,9 +307,9 @@ class TypeBuilder { } fir::SequenceType::Shape trivialShape(int size) { - fir::SequenceType::Bounds bounds; + fir::SequenceType::Shape bounds; bounds.emplace_back(size); - return {bounds}; + return bounds; } // some sequence of `n` bytes From ff391d670158386a0bb750b53bae5e600ae87b60 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Tue, 19 Nov 2019 07:32:19 -0800 Subject: [PATCH 032/123] fix .NOT. lowering and clean warning --- lib/burnside/convert-expr.cc | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index ea7cd324a53e..7f6a141198b6 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -117,11 +117,19 @@ class ExprLowering { } /// Generate a logical/boolean constant of `value` + M::Type getMLIRlogicalType() { + return M::IntegerType::get(1, builder.getContext()); + } + M::Value *genMLIRLogicalConstant(M::MLIRContext *context, bool value) { + auto attr{builder.getIntegerAttr(getMLIRlogicalType(), value ? 1 : 0)}; + return builder.create(getLoc(), getMLIRlogicalType(), attr) + .getResult(); + } template M::Value *genLogicalConstant(M::MLIRContext *context, bool value) { - auto attr{builder.getBoolAttr(value)}; - M::Type logTy{getFIRType(context, defaults, LogicalCat, KIND)}; - auto res{builder.create(getLoc(), logTy, attr)}; + auto mlirCst{genMLIRLogicalConstant(context, value)}; + M::Type firLogicalTy{getFIRType(context, defaults, LogicalCat, KIND)}; + auto res{builder.create(getLoc(), firLogicalTy, mlirCst)}; return res.getResult(); } @@ -394,9 +402,15 @@ class ExprLowering { return builder.create(getLoc(), ty, genval(convert.left())); } template M::Value *genval(Ev::Parentheses const &) { TODO(); } + template M::Value *genval(const Ev::Not &op) { auto *context{builder.getContext()}; - return createBinaryOp(op, genLogicalConstant(context, 1)); + auto mlirLogical{builder.create( + getLoc(), getMLIRlogicalType(), genval(op.left()))}; + auto i1One{genMLIRLogicalConstant(context, 1)}; + auto mlirRes{builder.create(getLoc(), mlirLogical, i1One)}; + auto firTy{getFIRType(builder.getContext(), defaults, LogicalCat, KIND)}; + return builder.create(getLoc(), firTy, mlirRes).getResult(); } template M::Value *genval(Ev::LogicalOperation const &op) { @@ -412,6 +426,11 @@ class ExprLowering { case Ev::LogicalOperator::Neqv: result = createCompareOp(op, M::CmpIPredicate::ne); break; + case Ev::LogicalOperator::Not: + // LogicalOperations are binary operations. Expr for Not is + // evaluate::Not. + assert(false); + break; } if (!result) { assert(false && "unhandled logical operation"); From 0f35e22698a1c43520339e6169fdf5ffae5eed5d Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 19 Nov 2019 11:07:38 -0800 Subject: [PATCH 033/123] add lowering of fir.loop --- include/fir/Transforms/Passes.h | 6 +- lib/fir/Tilikum.cpp | 40 ++---- lib/fir/Transforms/CMakeLists.txt | 1 + lib/fir/Transforms/RewriteLoop.cpp | 193 +++++++++++++++++++++++++++++ tools/tco/tco.cpp | 7 ++ 5 files changed, 213 insertions(+), 34 deletions(-) create mode 100644 lib/fir/Transforms/RewriteLoop.cpp diff --git a/include/fir/Transforms/Passes.h b/include/fir/Transforms/Passes.h index faafc64608ed..a33a570464ba 100644 --- a/include/fir/Transforms/Passes.h +++ b/include/fir/Transforms/Passes.h @@ -21,6 +21,7 @@ namespace mlir { class FuncOp; template class OpPassBase; +class Pass; } // namespace mlir namespace fir { @@ -28,10 +29,13 @@ namespace fir { /// Effects aware CSE pass std::unique_ptr> createCSEPass(); +/// Convert FIR loop constructs to the Affine dialect +std::unique_ptr createPromoteToAffinePass(); + /// Convert `fir.loop` and `fir.where` to `loop.for` and `loop.if`. This /// conversion enables the `createLowerToCFGPass` to transform these to CFG /// form. -std::unique_ptr> createFirToLoopPass(); +std::unique_ptr createLowerToLoopPass(); /// A pass to convert the FIR dialect from "Mem-SSA" form to "Reg-SSA" /// form. This pass is a port of LLVM's mem2reg pass, but modified for the FIR diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index 36db1a3dbe9d..a717b95850b9 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -1385,19 +1385,6 @@ struct LoadOpConversion : public FIROpConversion { } }; -// abstract loop construct -struct LoopOpConversion : public FIROpConversion { - using FIROpConversion::FIROpConversion; - - M::PatternMatchResult - matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { - auto loop = M::cast(op); - TODO(loop); - return matchSuccess(); - } -}; - // FIXME: how do we want to enforce this in LLVM-IR? struct NoReassocOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -1667,19 +1654,6 @@ struct UnreachableOpConversion : public FIROpConversion { } }; -// abstract conditional construct -struct WhereOpConversion : public FIROpConversion { - using FIROpConversion::FIROpConversion; - - M::PatternMatchResult - matchAndRewrite(M::Operation *op, OperandTy operands, - M::ConversionPatternRewriter &rewriter) const override { - auto where = M::cast(op); - TODO(where); - return matchSuccess(); - } -}; - // // Primitive operations on Real (floating-point) types // @@ -1948,13 +1922,13 @@ struct FIRToLLVMLoweringPass : public M::ModulePass { FirEndOpConversion, ExtractValueOpConversion, FreeMemOpConversion, GenDimsOpConversion, GenTypeDescOpConversion, GlobalEntryOpConversion, GlobalOpConversion, InsertValueOpConversion, LenParamIndexOpConversion, - LoadOpConversion, LoopOpConversion, ModfOpConversion, MulcOpConversion, - MulfOpConversion, NegcOpConversion, NegfOpConversion, - NoReassocOpConversion, SelectCaseOpConversion, SelectOpConversion, - SelectRankOpConversion, SelectTypeOpConversion, StoreOpConversion, - SubcOpConversion, SubfOpConversion, UnboxCharOpConversion, - UnboxOpConversion, UnboxProcOpConversion, UndefOpConversion, - UnreachableOpConversion, WhereOpConversion>(&context, typeConverter); + LoadOpConversion, ModfOpConversion, MulcOpConversion, MulfOpConversion, + NegcOpConversion, NegfOpConversion, NoReassocOpConversion, + SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion, + SelectTypeOpConversion, StoreOpConversion, SubcOpConversion, + SubfOpConversion, UnboxCharOpConversion, UnboxOpConversion, + UnboxProcOpConversion, UndefOpConversion, UnreachableOpConversion>( + &context, typeConverter); M::populateStdToLLVMConversionPatterns(typeConverter, patterns); M::ConversionTarget target{context}; target.addLegalDialect(); diff --git a/lib/fir/Transforms/CMakeLists.txt b/lib/fir/Transforms/CMakeLists.txt index a8ddb6d71ba9..6c0ad1179d2e 100644 --- a/lib/fir/Transforms/CMakeLists.txt +++ b/lib/fir/Transforms/CMakeLists.txt @@ -15,6 +15,7 @@ add_llvm_library(FIRTransforms CSE.cpp MemToReg.cpp + RewriteLoop.cpp ) add_dependencies(FIRTransforms FIROpsIncGen) diff --git a/lib/fir/Transforms/RewriteLoop.cpp b/lib/fir/Transforms/RewriteLoop.cpp new file mode 100644 index 000000000000..7f8a065a5857 --- /dev/null +++ b/lib/fir/Transforms/RewriteLoop.cpp @@ -0,0 +1,193 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fir/FIRDialect.h" +#include "fir/FIROps.h" +#include "fir/Transforms/Passes.h" +#include "mlir/Dialect/AffineOps/AffineOps.h" +#include "mlir/Dialect/LoopOps/LoopOps.h" +#include "mlir/Dialect/StandardOps/Ops.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Transforms/DialectConversion.h" +#include "llvm/Support/CommandLine.h" +#include + +namespace M = mlir; + +/// disable FIR to affine dialect conversion +static llvm::cl::opt + ClDisableAffinePromo("disable-affine-promotion", + llvm::cl::desc("disable FIR to Affine pass"), + llvm::cl::init(false)); + +/// disable FIR to loop dialect conversion +static llvm::cl::opt + ClDisableLoopConversion("disable-loop-conversion", + llvm::cl::desc("disable FIR to Loop pass"), + llvm::cl::init(false)); + +using namespace fir; + +namespace { + +template +class OpRewrite : public M::RewritePattern { +public: + explicit OpRewrite(M::MLIRContext *ctx) + : RewritePattern(FROM::getOperationName(), 1, ctx) {} +}; + +/// Convert `fir.loop` to `affine.for` +class AffineLoopConv : public OpRewrite { +public: + using OpRewrite::OpRewrite; +}; + +/// Convert `fir.where` to `affine.if` +class AffineWhereConv : public OpRewrite { +public: + using OpRewrite::OpRewrite; +}; + +/// Promote fir.loop and fir.where to affine.for and affine.if, in the cases +/// where such a promotion is possible. +class AffineDialectPromotion : public M::FunctionPass { +public: + void runOnFunction() override { + if (ClDisableAffinePromo) + return; + + auto *context{&getContext()}; + M::OwningRewritePatternList patterns; + patterns.insert(context); + M::ConversionTarget target{*context}; + target.addLegalDialect(); + // target.addDynamicallyLegalOp(); + + // apply the patterns + if (M::failed(M::applyPartialConversion(getFunction(), target, + std::move(patterns)))) { + M::emitError(M::UnknownLoc::get(context), + "error in converting to affine dialect\n"); + signalPassFailure(); + } + } +}; + +// Conversion to the MLIR loop dialect +// +// FIR loops that cannot be converted to the affine dialect will remain as +// `fir.loop` operations. These can be converted to `loop.for` operations. MLIR +// includes a pass to lower `loop.for` operations to a CFG. + +template +class OpConversion : public M::ConversionPattern { +public: + explicit OpConversion(M::MLIRContext *ctx) + : ConversionPattern(FROM::getOperationName(), 1, ctx) {} +}; + +/// Convert `fir.loop` to `loop.for` +class LoopLoopConv : public OpConversion { +public: + using OpConversion::OpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, llvm::ArrayRef operands, + M::ConversionPatternRewriter &rewriter) const override { + auto loop = M::cast(op); + auto *low = loop.lowerBound(); + auto *high = loop.upperBound(); + auto optStep = loop.optstep(); + auto loc = loop.getLoc(); + M::Value *step; + if (optStep.begin() != optStep.end()) { + step = *optStep.begin(); + } else { + auto conStep = loop.constep(); + step = rewriter.create( + loc, conStep.hasValue() ? conStep.getValue().getSExtValue() : 1); + } + auto f = rewriter.create(loc, low, high, step); + f.region().getBlocks().clear(); + rewriter.inlineRegionBefore(loop.region(), f.region(), f.region().end()); + rewriter.eraseOp(op); + return matchSuccess(); + } +}; + +/// Convert `fir.where` to `loop.if` +class LoopWhereConv : public OpConversion { +public: + using OpConversion::OpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, llvm::ArrayRef operands, + M::ConversionPatternRewriter &rewriter) const override { + auto where = M::cast(op); + return matchSuccess(); + } +}; + +class LoopFirEndConv : public OpConversion { +public: + using OpConversion::OpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, llvm::ArrayRef operands, + M::ConversionPatternRewriter &rewriter) const override { + rewriter.replaceOpWithNewOp(op); + return matchSuccess(); + } +}; + +/// Convert `fir.loop` and `fir.where` to `loop.for` and `loop.if`. +class LoopDialectConversion : public M::FunctionPass { +public: + void runOnFunction() override { + if (ClDisableLoopConversion) + return; + + auto *context{&getContext()}; + M::OwningRewritePatternList patterns; + patterns.insert(context); + M::ConversionTarget target{*context}; + target.addLegalDialect(); + target.addIllegalOp(); + + // apply the patterns + if (M::failed(M::applyPartialConversion(getFunction(), target, + std::move(patterns)))) { + M::emitError(M::UnknownLoc::get(context), + "error in converting to MLIR loop dialect\n"); + signalPassFailure(); + } + } +}; + +} // namespace + +/// Convert FIR loop constructs to the Affine dialect +std::unique_ptr fir::createPromoteToAffinePass() { + return std::make_unique(); +} + +/// Convert `fir.loop` and `fir.where` to `loop.for` and `loop.if`. This +/// conversion enables the `createLowerToCFGPass` to transform these to CFG +/// form. +std::unique_ptr fir::createLowerToLoopPass() { + return std::make_unique(); +} diff --git a/tools/tco/tco.cpp b/tools/tco/tco.cpp index ca68472abb78..b524dd31aa1f 100644 --- a/tools/tco/tco.cpp +++ b/tools/tco/tco.cpp @@ -20,6 +20,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" +#include "mlir/Conversion/LoopToStandard/ConvertLoopToStandard.h" #include "mlir/IR/MLIRContext.h" #include "mlir/IR/Module.h" #include "mlir/Parser.h" @@ -68,7 +69,13 @@ int compileFIR() { mlir::PassManager pm{context.get()}; pm.addPass(fir::createMemToRegPass()); pm.addPass(fir::createCSEPass()); + // convert fir dialect to affine + pm.addPass(fir::createPromoteToAffinePass()); + // convert fir dialect to loop + pm.addPass(fir::createLowerToLoopPass()); pm.addPass(fir::createFIRToStdPass()); + // convert loop dialect to standard + pm.addPass(mlir::createLowerToCFGPass()); pm.addPass(fir::createFIRToLLVMPass()); pm.addPass(fir::createLLVMDialectToLLVMPass(ClOutput)); if (mlir::succeeded(pm.run(*owningRef))) { From 78b74d5cf7d153f19da7e82e25bbcb44f14e9fab Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 19 Nov 2019 12:36:27 -0800 Subject: [PATCH 034/123] rename fe-helper to convert-type --- .clang-format | 2 +- lib/burnside/CMakeLists.txt | 2 +- lib/burnside/bridge.cc | 8 ++++---- lib/burnside/builder.cc | 2 +- lib/burnside/complex-handler.h | 8 ++++---- lib/burnside/convert-expr.cc | 8 ++++---- lib/burnside/{fe-helper.cc => convert-type.cc} | 6 +++--- lib/burnside/{fe-helper.h => convert-type.h} | 10 +++++++--- lib/burnside/intrinsics.cc | 4 ++-- 9 files changed, 27 insertions(+), 23 deletions(-) rename lib/burnside/{fe-helper.cc => convert-type.cc} (99%) rename lib/burnside/{fe-helper.h => convert-type.h} (94%) diff --git a/.clang-format b/.clang-format index cea0a90a1407..973887d48027 100644 --- a/.clang-format +++ b/.clang-format @@ -16,7 +16,7 @@ FixNamespaceComments: false IncludeCategories: - Regex: '^<' Priority: 4 - - Regex: '^"(llvm|llvm-c|clang|clang-c|mlir|mlir-c)/' + - Regex: '^"(llvm|llvm-c|clang|clang-c|fir|mlir|mlir-c)/' Priority: 3 - Regex: '^"\.\./' Priority: 2 diff --git a/lib/burnside/CMakeLists.txt b/lib/burnside/CMakeLists.txt index c4b1c3e9b6ca..9a7e83937775 100644 --- a/lib/burnside/CMakeLists.txt +++ b/lib/burnside/CMakeLists.txt @@ -18,7 +18,7 @@ add_library(FortranBurnside bridge.cc builder.cc convert-expr.cc - fe-helper.cc + convert-type.cc flattened.cc intrinsics.cc runtime.cc diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 87922bfeba47..19b5f59647b6 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -15,16 +15,16 @@ #include "bridge.h" #include "builder.h" #include "convert-expr.h" -#include "fe-helper.h" -#include "fir/FIRDialect.h" -#include "fir/FIROps.h" -#include "fir/FIRType.h" +#include "convert-type.h" #include "flattened.h" #include "intrinsics.h" #include "io.h" #include "runtime.h" #include "../parser/parse-tree-visitor.h" #include "../semantics/tools.h" +#include "fir/FIRDialect.h" +#include "fir/FIROps.h" +#include "fir/FIRType.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/StandardOps/Ops.h" #include "mlir/IR/Builders.h" diff --git a/lib/burnside/builder.cc b/lib/burnside/builder.cc index 409203483f2b..e789d2843a31 100644 --- a/lib/burnside/builder.cc +++ b/lib/burnside/builder.cc @@ -14,7 +14,7 @@ #include "builder.h" #include "bridge.h" -#include "fe-helper.h" +#include "convert-type.h" #include "llvm/ADT/StringRef.h" #include "mlir/IR/Module.h" #include "mlir/IR/Value.h" diff --git a/lib/burnside/complex-handler.h b/lib/burnside/complex-handler.h index 6c408c0e9879..4400e98a2b60 100644 --- a/lib/burnside/complex-handler.h +++ b/lib/burnside/complex-handler.h @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FORTRAN_BURNSIDE_COMPLEX_H_ -#define FORTRAN_BURNSIDE_COMPLEX_H_ +#ifndef FORTRAN_BURNSIDE_COMPLEX_HANDLER_H_ +#define FORTRAN_BURNSIDE_COMPLEX_HANDLER_H_ /// [Coding style](https://llvm.org/docs/CodingStandards.html) -#include "fe-helper.h" +#include "convert-type.h" #include "fir/FIROps.h" #include "fir/FIRType.h" @@ -107,4 +107,4 @@ class ComplexHandler { }; } -#endif // FORTRAN_BURNSIDE_COMPLEX_H_ +#endif // FORTRAN_BURNSIDE_COMPLEX_HANDLER_H_ diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 7f6a141198b6..0f2343f5f264 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -15,10 +15,7 @@ #include "convert-expr.h" #include "builder.h" #include "complex-handler.h" -#include "fe-helper.h" -#include "fir/FIRDialect.h" -#include "fir/FIROps.h" -#include "fir/FIRType.h" +#include "convert-type.h" #include "intrinsics.h" #include "runtime.h" #include "../common/default-kinds.h" @@ -28,6 +25,9 @@ #include "../semantics/expression.h" #include "../semantics/symbol.h" #include "../semantics/type.h" +#include "fir/FIRDialect.h" +#include "fir/FIROps.h" +#include "fir/FIRType.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/IR/Type.h" diff --git a/lib/burnside/fe-helper.cc b/lib/burnside/convert-type.cc similarity index 99% rename from lib/burnside/fe-helper.cc rename to lib/burnside/convert-type.cc index 0e5083be440a..1b5cbe5575b3 100644 --- a/lib/burnside/fe-helper.cc +++ b/lib/burnside/convert-type.cc @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "fe-helper.h" +#include "convert-type.h" #include "bridge.h" -#include "fir/FIRType.h" #include "../semantics/expression.h" #include "../semantics/tools.h" #include "../semantics/type.h" +#include "fir/FIRType.h" #include "mlir/IR/Builders.h" #include "mlir/IR/Location.h" #include "mlir/IR/MLIRContext.h" @@ -288,7 +288,7 @@ class TypeBuilder { case DerivedCat: TODO(); break; } } else if (auto *tySpec{type->AsDerived()}) { - (void)tySpec; // FIXME + (void)tySpec; // FIXME TODO(); } else { assert(false && "type spec not found"); diff --git a/lib/burnside/fe-helper.h b/lib/burnside/convert-type.h similarity index 94% rename from lib/burnside/fe-helper.h rename to lib/burnside/convert-type.h index bc7352339212..34218eb87b97 100644 --- a/lib/burnside/fe-helper.h +++ b/lib/burnside/convert-type.h @@ -12,12 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FORTRAN_BURNSIDE_FE_HELPER_H_ -#define FORTRAN_BURNSIDE_FE_HELPER_H_ +#ifndef FORTRAN_BURNSIDE_CONVERT_TYPE_H_ +#define FORTRAN_BURNSIDE_CONVERT_TYPE_H_ /// Traversal and conversion of Fortran type data structures into the FIR /// dialect of MLIR. /// +/// Lowering of parse tree TYPE, KIND, ATTRIBUTE information to the FIR type +/// system. +/// /// [Coding style](https://llvm.org/docs/CodingStandards.html) #include "../common/Fortran.h" @@ -108,4 +111,5 @@ mlir::Type convertReal(mlir::MLIRContext *context, int KIND); } // burnside } // Fortran -#endif // FORTRAN_BURNSIDE_FE_HELPER_H_ + +#endif // FORTRAN_BURNSIDE_CONVERT_TYPE_H_ diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index a80471fa4a12..e5beb1432e4a 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -15,10 +15,10 @@ #include "intrinsics.h" #include "builder.h" #include "complex-handler.h" -#include "fe-helper.h" +#include "convert-type.h" +#include "runtime.h" #include "fir/FIROps.h" #include "fir/FIRType.h" -#include "runtime.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/Twine.h" #include From 18d8555bf23b402921bd3e7b212edb4b9ba9d25d Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 19 Nov 2019 14:10:33 -0800 Subject: [PATCH 035/123] get rid of singleton; start threading internal name mangler --- include/fir/FIROps.td | 6 ++++-- include/fir/InternalNames.h | 2 +- include/fir/Tilikum/Tilikum.h | 5 ++++- lib/burnside/bridge.cc | 13 +++++-------- lib/burnside/bridge.h | 22 ++++++++++------------ lib/fir/Tilikum.cpp | 15 +++++++++------ tools/bbc/CMakeLists.txt | 22 ++++++++++------------ tools/bbc/bbc.cc | 20 +++++++++++--------- tools/tco/CMakeLists.txt | 24 ++++++++++++------------ tools/tco/tco.cpp | 4 +++- 10 files changed, 69 insertions(+), 64 deletions(-) diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index f13d626baae1..aa7faa89deb9 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -493,7 +493,7 @@ class fir_IntegralSwitchTerminatorOp dests; llvm::SmallVector, 4> destArgs; while (true) { - M::IntegerAttr ivalue; + M::Attribute ivalue; // Integer or Unit M::Block *dest; llvm::SmallVector destArg; llvm::SmallVector temp; @@ -789,6 +789,7 @@ def fir_UnreachableOp : fir_Op<"unreachable", [Terminator]> { let parser = [{ return M::success(); }]; + let printer = [{ p << getOperationName(); }]; @@ -1281,7 +1282,7 @@ def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoSideEffect]>, if (!recty.dyn_cast()) return M::failure(); result.addAttribute(typeAttrName(), M::TypeAttr::get(recty)); - if (!parser.parseLParen()) { + if (!parser.parseOptionalLParen()) { L::SmallVector operands; L::SmallVector types; auto loc = parser.getNameLoc(); @@ -1549,6 +1550,7 @@ def fir_WhereOp : fir_Op<"where", [ImplicitFirTerminator]> { } p.printOptionalAttrDict(getAttrs()); }]; + let verifier = [{ for (auto ®ion : getOperation()->getRegions()) { if (region.empty()) diff --git a/include/fir/InternalNames.h b/include/fir/InternalNames.h index 9f00d150cd53..83ad0b9e49ef 100644 --- a/include/fir/InternalNames.h +++ b/include/fir/InternalNames.h @@ -32,7 +32,7 @@ namespace fir { struct NameMangler { enum class IntrinsicType { CHARACTER, COMPLEX, INTEGER, LOGICAL, REAL }; - NameMangler(); + NameMangler() = default; /// Mangle a common block name llvm::Twine doCommonBlock(llvm::StringRef name); diff --git a/include/fir/Tilikum/Tilikum.h b/include/fir/Tilikum/Tilikum.h index dca126e0491e..25d8205a491c 100644 --- a/include/fir/Tilikum/Tilikum.h +++ b/include/fir/Tilikum/Tilikum.h @@ -26,11 +26,14 @@ class Pass; namespace fir { +struct NameMangler; + /// Convert FIR to the LLVM IR dialect std::unique_ptr createFIRToLLVMPass(); /// Convert the LLVM IR dialect to LLVM-IR proper -std::unique_ptr createLLVMDialectToLLVMPass(llvm::StringRef output); +std::unique_ptr +createLLVMDialectToLLVMPass(llvm::StringRef output, NameMangler &mangler); } // namespace fir diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 19b5f59647b6..b79bcd3831ba 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -25,6 +25,7 @@ #include "fir/FIRDialect.h" #include "fir/FIROps.h" #include "fir/FIRType.h" +#include "fir/InternalNames.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/StandardOps/Ops.h" #include "mlir/IR/Builders.h" @@ -49,8 +50,6 @@ namespace { using SomeExpr = Ev::Expr; -std::unique_ptr bridgeInstance; - constexpr bool isStopStmt(Pa::StopStmt::Kind kind) { return kind == Pa::StopStmt::Kind::Stop; } @@ -1028,7 +1027,8 @@ void FIRConverter::translateRoutine( } // namespace -void Br::crossBurnsideBridge(BurnsideBridge &bridge, const Pa::Program &prg) { +void Br::crossBurnsideBridge( + BurnsideBridge &bridge, const Pa::Program &prg, fir::NameMangler &mangler) { FIRConverter converter{bridge}; Walk(prg, converter); } @@ -1052,11 +1052,8 @@ Br::BurnsideBridge::BurnsideBridge( M::ModuleOp::create(M::UnknownLoc::get(context.get()))); } -void Br::instantiateBurnsideBridge( +BurnsideBridge Br::getBurnsideBridge( const Co::IntrinsicTypeDefaultKinds &defaultKinds, const Pa::CookedSource *cooked) { - auto p{BurnsideBridge::create(defaultKinds, cooked)}; - bridgeInstance.swap(p); + return BurnsideBridge::create(defaultKinds, cooked); } - -BurnsideBridge &Br::getBridge() { return *bridgeInstance.get(); } diff --git a/lib/burnside/bridge.h b/lib/burnside/bridge.h index b4614a050099..00955159f8f0 100644 --- a/lib/burnside/bridge.h +++ b/lib/burnside/bridge.h @@ -38,17 +38,18 @@ class Module; class SourceMgr; } +namespace fir { +struct NameMangler; +} + namespace Fortran::burnside { -/// An instance of BurnsideBridge is a singleton that owns the state of the -/// bridge class BurnsideBridge { public: - static std::unique_ptr create( + static BurnsideBridge create( const common::IntrinsicTypeDefaultKinds &defaultKinds, const parser::CookedSource *cooked) { - BurnsideBridge *p = new BurnsideBridge{defaultKinds, cooked}; - return std::unique_ptr{p}; + return BurnsideBridge{defaultKinds, cooked}; } mlir::MLIRContext &getMLIRContext() { return *context.get(); } @@ -77,20 +78,17 @@ class BurnsideBridge { }; /// Cross the bridge from the Fortran parse-tree, etc. to FIR+OpenMP+MLIR -void crossBurnsideBridge( - BurnsideBridge &bridge, const parser::Program &program); +void crossBurnsideBridge(BurnsideBridge &bridge, const parser::Program &program, + fir::NameMangler &mangler); /// Bridge from MLIR to LLVM-IR std::unique_ptr LLVMBridge(mlir::ModuleOp &module); -/// instantiate the BURNSIDE bridge singleton -void instantiateBurnsideBridge( +/// Get an instance of the Burnside bridge +BurnsideBridge getBurnsideBridge( const common::IntrinsicTypeDefaultKinds &defaultKinds, const parser::CookedSource *cooked = nullptr); -/// get the burnside bridge singleton -BurnsideBridge &getBridge(); - } // Fortran::burnside #endif // FORTRAN_BURNSIDE_BRIDGE_H_ diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index a717b95850b9..fe517f559513 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -17,6 +17,7 @@ #include "fir/FIRDialect.h" #include "fir/FIROps.h" #include "fir/FIRType.h" +#include "fir/InternalNames.h" #include "fir/KindMapping.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h" @@ -1951,6 +1952,9 @@ struct FIRToLLVMLoweringPass : public M::ModulePass { /// Lower from LLVM IR dialect to proper LLVM-IR and dump the module struct LLVMIRLoweringPass : public M::ModulePass { + LLVMIRLoweringPass(L::StringRef outputName, NameMangler &mangler) + : outputName{outputName}, mangler{mangler} {} + void runOnModule() override { if (ClDisableLLVM) return; @@ -1970,14 +1974,13 @@ struct LLVMIRLoweringPass : public M::ModulePass { signalPassFailure(); } - void setOutputName(L::StringRef output) { outputName = output; } - private: void genDispatchTableMap() { // TODO } L::StringRef outputName; + NameMangler &mangler; }; } // namespace @@ -1986,8 +1989,8 @@ std::unique_ptr fir::createFIRToLLVMPass() { return std::make_unique(); } -std::unique_ptr fir::createLLVMDialectToLLVMPass(L::StringRef output) { - auto pass = std::make_unique(); - pass->setOutputName(output); - return pass; +std::unique_ptr +fir::createLLVMDialectToLLVMPass(L::StringRef output, + fir::NameMangler &nameMangler) { + return std::make_unique(output, nameMangler); } diff --git a/tools/bbc/CMakeLists.txt b/tools/bbc/CMakeLists.txt index 58e011fb18ed..3366006bfe55 100644 --- a/tools/bbc/CMakeLists.txt +++ b/tools/bbc/CMakeLists.txt @@ -12,30 +12,28 @@ # See the License for the specific language governing permissions and # limitations under the License. -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-string-conversion") - -add_executable(bbc - bbc.cc -) - set(LIBS FIR - FIRTransforms MLIRAffineOps MLIRLLVMIR MLIRLoopOps MLIRStandardOps ) -whole_archive_link(bbc ${LIBS}) -target_link_libraries(bbc +add_llvm_library(FirBbcLib bbc.cc) +target_link_libraries(FirBbcLib ${LIBS} FIRTransforms) + +set(EXE_LIBS + ${LIBS} + FIRTransforms FortranCommon FortranParser FortranEvaluate FortranSemantics FortranBurnside - FIRTransforms - ${LIBS} ) -install(TARGETS bbc DESTINATION bin) +add_llvm_tool(bbc bbc.cc) +llvm_update_compile_flags(bbc) +whole_archive_link(bbc ${LIBS}) +target_link_libraries(bbc PRIVATE FIR MLIRIR FirBbcLib ${EXE_LIBS} LLVMSupport) diff --git a/tools/bbc/bbc.cc b/tools/bbc/bbc.cc index 1cc749a6d23b..4ac7de37e0f3 100644 --- a/tools/bbc/bbc.cc +++ b/tools/bbc/bbc.cc @@ -14,16 +14,12 @@ // Temporary Fortran front end driver main program for development scaffolding. -#include "fir/FIRDialect.h" -#include "fir/Tilikum/Tilikum.h" -#include "fir/Transforms/Passes.h" -#include "fir/Transforms/StdConverter.h" #include "../../lib/burnside/bridge.h" #include "../../lib/burnside/convert-expr.h" +#include "../../lib/common/Fortran-features.h" #include "../../lib/common/default-kinds.h" #include "../../lib/parser/characters.h" #include "../../lib/parser/dump-parse-tree.h" -#include "../../lib/common/Fortran-features.h" #include "../../lib/parser/message.h" #include "../../lib/parser/parse-tree-visitor.h" #include "../../lib/parser/parse-tree.h" @@ -33,6 +29,11 @@ #include "../../lib/semantics/expression.h" #include "../../lib/semantics/semantics.h" #include "../../lib/semantics/unparse-with-symbols.h" +#include "fir/FIRDialect.h" +#include "fir/InternalNames.h" +#include "fir/Tilikum/Tilikum.h" +#include "fir/Transforms/Passes.h" +#include "fir/Transforms/StdConverter.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" @@ -267,10 +268,11 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, } // MLIR+FIR - Br::instantiateBurnsideBridge( + fir::NameMangler nameMangler; + auto burnside = Br::getBurnsideBridge( semanticsContext.defaultKinds(), &parsing.cooked()); - Br::crossBurnsideBridge(Br::getBridge(), parseTree); - mlir::ModuleOp mlirModule{Br::getBridge().getModule()}; + Br::crossBurnsideBridge(burnside, parseTree, nameMangler); + mlir::ModuleOp mlirModule{burnside.getModule()}; mlir::PassManager pm{mlirModule.getContext()}; if (driver.dumpHLFIR) { llvm::errs() << ";== 1 ==\n"; @@ -286,7 +288,7 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, pm.addPass(fir::createFIRToLLVMPass()); } if (driver.lowerToLLVMIR) { - pm.addPass(fir::createLLVMDialectToLLVMPass("a.ll")); + pm.addPass(fir::createLLVMDialectToLLVMPass("a.ll", nameMangler)); } if (mlir::succeeded(pm.run(mlirModule))) { if (driver.dumpFIR) { diff --git a/tools/tco/CMakeLists.txt b/tools/tco/CMakeLists.txt index 3e2ae6844ea4..4c927d8548c3 100644 --- a/tools/tco/CMakeLists.txt +++ b/tools/tco/CMakeLists.txt @@ -14,33 +14,33 @@ set(LIBS FIR - FIRTransforms - MLIRAffineToStandard - MLIRAnalysis + MLIRAffineOps MLIRLLVMIR - MLIRParser - MLIRPass - MLIRTransforms - MLIRSupport + MLIRLoopOps + MLIRStandardOps ) add_llvm_library(FirTcoLib tco.cpp) -target_link_libraries(FirTcoLib ${LIBS}) +target_link_libraries(FirTcoLib ${LIBS} FIRTransforms) set(EXE_LIBS ${LIBS} - MLIRAffineOps + FIRTransforms + MLIRAffineToStandard + MLIRAnalysis MLIRLoopToStandard MLIREDSC MLIRLinalg - MLIRLoopOps - MLIRStandardOps + MLIRParser + MLIRPass MLIRStandardToLLVM + MLIRSupport + MLIRTransforms MLIRVectorOps MLIRVectorToLLVM ) add_llvm_tool(tco tco.cpp) llvm_update_compile_flags(tco) -whole_archive_link(tco ${EXE_LIBS}) +whole_archive_link(tco ${LIBS}) target_link_libraries(tco PRIVATE FIR MLIRIR FirTcoLib ${EXE_LIBS} LLVMSupport) diff --git a/tools/tco/tco.cpp b/tools/tco/tco.cpp index b524dd31aa1f..1ce63241f2e0 100644 --- a/tools/tco/tco.cpp +++ b/tools/tco/tco.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "fir/FIRDialect.h" +#include "fir/InternalNames.h" #include "fir/Tilikum/Tilikum.h" #include "fir/Transforms/Passes.h" #include "fir/Transforms/StdConverter.h" @@ -66,6 +67,7 @@ int compileFIR() { owningRef->dump(); // run passes + fir::NameMangler mangler; mlir::PassManager pm{context.get()}; pm.addPass(fir::createMemToRegPass()); pm.addPass(fir::createCSEPass()); @@ -77,7 +79,7 @@ int compileFIR() { // convert loop dialect to standard pm.addPass(mlir::createLowerToCFGPass()); pm.addPass(fir::createFIRToLLVMPass()); - pm.addPass(fir::createLLVMDialectToLLVMPass(ClOutput)); + pm.addPass(fir::createLLVMDialectToLLVMPass(ClOutput, mangler)); if (mlir::succeeded(pm.run(*owningRef))) { errs() << ";== output ==\n"; owningRef->dump(); From 5e939307045b323feba1663a4cddb069f69c87d7 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Wed, 20 Nov 2019 10:40:39 -0800 Subject: [PATCH 036/123] simplify syntax for alloca and allocmem --- include/fir/FIROps.td | 61 +++++++++++++++++++++++++------------------ lib/fir/FIROps.cpp | 21 +++++++++++++-- test/fir/alloc.fir | 8 +++--- test/fir/arrayset.fir | 16 ++++++++++++ test/fir/fir-ops.fir | 12 ++++----- 5 files changed, 80 insertions(+), 38 deletions(-) create mode 100644 test/fir/arrayset.fir diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index aa7faa89deb9..b10e5c9c901e 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -211,9 +211,13 @@ class fir_AllocatableOp traits =[]> : parser.resolveOperands(operands, builder.getIndexType(), parser.getNameLoc(), result.operands)) return M::failure(); - M::Type restype; + M::Type restype = wrapResultType(intype); + if (!restype) { + parser.emitError(parser.getNameLoc(), "invalid allocate type: ") + << intype; + return M::failure(); + } if (parser.parseOptionalAttrDict(result.attributes) || - parser.parseColonType(restype) || parser.addTypeToList(restype, result.types)) return M::failure(); return M::success(); @@ -231,10 +235,9 @@ class fir_AllocatableOp traits =[]> : p.printOperand(sh); } p.printOptionalAttrDict(getAttrs(), {"in_type", lenpName()}); - p << " : " << getType(); }]; - let extraClassDeclaration = [{ + string extraAllocClassDeclaration = [{ constexpr static llvm::StringRef lenpName() { return "len_param_count"; } mlir::Type getAllocatedType(); bool hasLenParams() { return bool{getAttr(lenpName())}; } @@ -264,19 +267,21 @@ def fir_AllocaOp : fir_AllocatableOp<"alloca"> { object has an undefined 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. + + %0 = fir.alloca !fir.array<10xf32> + %1 = fir.coordinate_of %0, %c0 : (!fir.ref>, index) -> !fir.ref }]; let verifier = [{ - M::Type inType = getAttrOfType("in_type").getValue(); M::Type outType = getType(); - if (auto ref = outType.dyn_cast()) { - if (inType != ref.getEleTy()) - return emitOpError("input type and !fir.ref element type mismatch"); - } else { + if (!outType.dyn_cast()) return emitOpError("must be a !fir.ref type"); - } return M::success(); }]; + + let extraClassDeclaration = extraAllocClassDeclaration#[{ + static mlir::Type wrapResultType(mlir::Type intype); + }]; } def fir_LoadOp : fir_OneResultOp<"load", []>, @@ -399,19 +404,21 @@ def fir_AllocMemOp : fir_AllocatableOp<"allocmem"> { 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<10xf32> + fir.freemem %0 : !fir.heap> }]; let verifier = [{ - M::Type inType = getAttrOfType("in_type").getValue(); M::Type outType = getType(); - if (auto ref = outType.dyn_cast()) { - if (inType != ref.getEleTy()) - return emitOpError("input type and !fir.heap element type mismatch"); - } else { + if (!outType.dyn_cast()) return emitOpError("must be a !fir.heap type"); - } return M::success(); }]; + + let extraClassDeclaration = extraAllocClassDeclaration#[{ + static mlir::Type wrapResultType(mlir::Type intype); + }]; } def fir_FreeMemOp : fir_Op<"freemem", []>, @@ -422,7 +429,11 @@ def fir_FreeMemOp : fir_Op<"freemem", []>, 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. + in the undefined state as undefined behavior. This includes aliasing + references, such as the result of an `fir.embox`. + + %0 = fir.allocmem !fir.array<10xf32> + fir.freemem %0 : !fir.heap> }]; let parser = [{ @@ -440,7 +451,7 @@ def fir_FreeMemOp : fir_Op<"freemem", []>, p << getOperationName() << ' '; p.printOperand(heapref()); p.printOptionalAttrDict(getAttrs(), {}); - p << " : " << getOperand()->getType(); + p << " : " << getOperand()->getType(); }]; } @@ -1256,7 +1267,7 @@ def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoSideEffect]>, 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 + or `fir.insert_value` instructions to compute (abstract) addresses of subobjects. }]; @@ -1323,7 +1334,7 @@ def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoSideEffect]>, }]; } -def fir_GenDimsOp : fir_OneResultOp<"gendims", [NoSideEffect]>, +def fir_GenDimsOp : fir_OneResultOp<"gendims", [NoSideEffect]>, Arguments<(ins Variadic:$triples)> { let summary = "generate a value of type `!fir.dims`"; @@ -1515,9 +1526,7 @@ def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> { void setStep(mlir::Value *step) { getOperation()->setOperand(2, step); } - static char const* getStepKeyword() { - return "step"; - } + static char const* getStepKeyword() { return "step"; } }]; } @@ -1626,7 +1635,7 @@ def fir_DispatchOp : fir_Op<"dispatch", []>, return M::success(); }]; - let printer = [{ + let printer = [{ p << getOperationName() << ' ' << getAttr("method") << '('; p.printOperand(object()); if (arg_operand_begin() != arg_operand_end()) { @@ -1959,7 +1968,7 @@ def fir_GlobalEntryOp : fir_Op<"global_entry", []>, result.addAttribute("in_type", M::TypeAttr::get(type)); return M::success(); }]; - let printer = [{ + let printer = [{ p << getOperationName() << ' ' << getAttr("field") << ", "; p.printOperand(constant()); p.printOptionalAttrDict(getAttrs(), {"in_type", "field"}); @@ -1995,7 +2004,7 @@ def fir_DTEntryOp : fir_Op<"dt_entry", []>, return M::failure(); return M::success(); }]; - let printer = [{ + let printer = [{ p << getOperationName() << ' ' << getAttr("method") << ", " << getAttr("proc"); }]; diff --git a/lib/fir/FIROps.cpp b/lib/fir/FIROps.cpp index bdff4e57f8df..55e2da3cffee 100644 --- a/lib/fir/FIROps.cpp +++ b/lib/fir/FIROps.cpp @@ -29,21 +29,38 @@ namespace fir { // AllocaOp M::Type AllocaOp::getAllocatedType() { - // return getAttrOfType("in_type").getType(); return getType().cast().getEleTy(); } +/// Create a legal memory reference as return type +M::Type AllocaOp::wrapResultType(M::Type intype) { + // FIR semantics: memory references to memory references are disallowed + if (intype.dyn_cast()) + return {}; + return ReferenceType::get(intype); +} + M::Type AllocaOp::getRefTy(M::Type ty) { return ReferenceType::get(ty); } // AllocMemOp M::Type AllocMemOp::getAllocatedType() { - // return getAttrOfType("in_type").getType(); return getType().cast().getEleTy(); } M::Type AllocMemOp::getRefTy(M::Type ty) { return HeapType::get(ty); } +/// Create a legal heap reference as return type +M::Type AllocMemOp::wrapResultType(M::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 diff --git a/test/fir/alloc.fir b/test/fir/alloc.fir index a1081c123a44..e7c0d663c128 100644 --- a/test/fir/alloc.fir +++ b/test/fir/alloc.fir @@ -1,21 +1,21 @@ func @f1() -> !fir.ref { - %1 = fir.alloca i32 : !fir.ref + %1 = fir.alloca i32 return %1 : !fir.ref } func @f2() -> !fir.ref { %0 = constant 100 : index - %1 = fir.alloca i32, %0 : !fir.ref + %1 = fir.alloca i32, %0 return %1 : !fir.ref } func @f3() -> !fir.heap { - %1 = fir.allocmem i32 : !fir.heap + %1 = fir.allocmem i32 return %1 : !fir.heap } func @f4() -> !fir.heap { %0 = constant 100 : index - %1 = fir.allocmem i32, %0 : !fir.heap + %1 = fir.allocmem i32, %0 return %1 : !fir.heap } diff --git a/test/fir/arrayset.fir b/test/fir/arrayset.fir new file mode 100644 index 000000000000..2f45aabb0da7 --- /dev/null +++ b/test/fir/arrayset.fir @@ -0,0 +1,16 @@ +func @x(%arr : !fir.ref>) { + %1 = constant 0 : index + %2 = constant 9 : index + %a = fir.alloca !fir.array<10xf32> + fir.loop %iv = %1 to %2 unordered { + %3 = fir.coordinate_of %arr, %iv : (!fir.ref>, index) -> !fir.ref + %4 = fir.load %3 : !fir.ref + %5 = fir.coordinate_of %a, %iv : (!fir.ref>, index) -> !fir.ref + fir.store %4 to %5 : !fir.ref + } { arrayset } + %6 = fir.embox %a : (!fir.ref>) -> !fir.box> + fir.call @y(%6) : (!fir.box>) -> () + return +} + +func @y(!fir.box>) -> () diff --git a/test/fir/fir-ops.fir b/test/fir/fir-ops.fir index 03eea8102aab..634e74e1edbe 100644 --- a/test/fir/fir-ops.fir +++ b/test/fir/fir-ops.fir @@ -45,13 +45,13 @@ func @nop() func @get_func() -> (() -> ()) func @instructions() { - %0 = fir.alloca !fir.array<10xi32> : !fir.ref> + %0 = fir.alloca !fir.array<10xi32> %1 = fir.load %0 : !fir.ref> - %2 = fir.alloca i32 : !fir.ref + %2 = fir.alloca i32 %3 = constant 22 : i32 fir.store %3 to %2 : !fir.ref %4 = fir.undefined i32 - %5 = fir.allocmem !fir.array<100xf32> : !fir.heap> + %5 = fir.allocmem !fir.array<100xf32> %6 = fir.embox %5 : (!fir.heap>) -> !fir.box> %7 = fir.box_addr %6 : (!fir.box>) -> !fir.ref> %c0 = constant 0 : index @@ -93,15 +93,15 @@ func @instructions() { } func @boxing_match() { - %0 = fir.alloca i32 : !fir.ref + %0 = fir.alloca i32 %1 = fir.embox %0 : (!fir.ref) -> !fir.box %2:6 = fir.unbox %1 : (!fir.box) -> (!fir.ref,i32,i32,!fir.tdesc,i32,!fir.dims<0>) %c8 = constant 8 : i32 - %3 = fir.alloca !fir.char<1> : !fir.ref> + %3 = fir.alloca !fir.char<1> %b3 = fir.undefined !fir.char<1> %4 = fir.emboxchar %3, %c8 : (!fir.ref>, i32) -> !fir.boxchar<1> %5:2 = fir.unboxchar %4 : (!fir.boxchar<1>) -> (!fir.ref>, i32) - %6 = fir.alloca !fir.type : !fir.ref> + %6 = fir.alloca !fir.type %a1 = fir.undefined !fir.type %z = constant 0 : i32 %c12 = constant 12 : i32 From db9a6ea094e3db82b6336d244259441de1576c00 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Wed, 20 Nov 2019 13:21:45 -0800 Subject: [PATCH 037/123] lowering of fir.where operation --- include/fir/FIROps.td | 4 +-- lib/fir/Transforms/RewriteLoop.cpp | 45 +++++++++++++++--------------- test/fir/loop.fir | 11 ++++++++ 3 files changed, 36 insertions(+), 24 deletions(-) create mode 100644 test/fir/loop.fir diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index b10e5c9c901e..981201b83be2 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -268,8 +268,8 @@ def fir_AllocaOp : fir_AllocatableOp<"alloca"> { name. The allocation may have a dynamic repetition count for allocating a sequence of locations for the specified type. - %0 = fir.alloca !fir.array<10xf32> - %1 = fir.coordinate_of %0, %c0 : (!fir.ref>, index) -> !fir.ref + %0 = fir.alloca f32 + %1 = fir.call @foo(%0) : (!fir.ref) -> i32 }]; let verifier = [{ diff --git a/lib/fir/Transforms/RewriteLoop.cpp b/lib/fir/Transforms/RewriteLoop.cpp index 7f8a065a5857..8a06f16df986 100644 --- a/lib/fir/Transforms/RewriteLoop.cpp +++ b/lib/fir/Transforms/RewriteLoop.cpp @@ -92,22 +92,13 @@ class AffineDialectPromotion : public M::FunctionPass { // `fir.loop` operations. These can be converted to `loop.for` operations. MLIR // includes a pass to lower `loop.for` operations to a CFG. -template -class OpConversion : public M::ConversionPattern { -public: - explicit OpConversion(M::MLIRContext *ctx) - : ConversionPattern(FROM::getOperationName(), 1, ctx) {} -}; - /// Convert `fir.loop` to `loop.for` -class LoopLoopConv : public OpConversion { +class LoopLoopConv : public M::OpRewritePattern { public: - using OpConversion::OpConversion; + using OpRewritePattern::OpRewritePattern; M::PatternMatchResult - matchAndRewrite(M::Operation *op, llvm::ArrayRef operands, - M::ConversionPatternRewriter &rewriter) const override { - auto loop = M::cast(op); + matchAndRewrite(LoopOp loop, M::PatternRewriter &rewriter) const override { auto *low = loop.lowerBound(); auto *high = loop.upperBound(); auto optStep = loop.optstep(); @@ -123,31 +114,41 @@ class LoopLoopConv : public OpConversion { auto f = rewriter.create(loc, low, high, step); f.region().getBlocks().clear(); rewriter.inlineRegionBefore(loop.region(), f.region(), f.region().end()); - rewriter.eraseOp(op); + rewriter.eraseOp(loop); return matchSuccess(); } }; /// Convert `fir.where` to `loop.if` -class LoopWhereConv : public OpConversion { +class LoopWhereConv : public M::OpRewritePattern { public: - using OpConversion::OpConversion; + using OpRewritePattern::OpRewritePattern; M::PatternMatchResult - matchAndRewrite(M::Operation *op, llvm::ArrayRef operands, - M::ConversionPatternRewriter &rewriter) const override { - auto where = M::cast(op); + matchAndRewrite(WhereOp where, M::PatternRewriter &rewriter) const override { + auto loc = where.getLoc(); + bool hasOtherRegion = !where.otherRegion().empty(); + auto cond = where.condition(); + auto ifOp = rewriter.create(loc, cond, hasOtherRegion); + rewriter.inlineRegionBefore(where.whereRegion(), &ifOp.thenRegion().back()); + ifOp.thenRegion().back().erase(); + if (hasOtherRegion) { + rewriter.inlineRegionBefore(where.otherRegion(), + &ifOp.elseRegion().back()); + ifOp.elseRegion().back().erase(); + } + rewriter.eraseOp(where); return matchSuccess(); } }; -class LoopFirEndConv : public OpConversion { +/// Replace FirEndOp with TerminatorOp +class LoopFirEndConv : public M::OpRewritePattern { public: - using OpConversion::OpConversion; + using OpRewritePattern::OpRewritePattern; M::PatternMatchResult - matchAndRewrite(M::Operation *op, llvm::ArrayRef operands, - M::ConversionPatternRewriter &rewriter) const override { + matchAndRewrite(FirEndOp op, M::PatternRewriter &rewriter) const override { rewriter.replaceOpWithNewOp(op); return matchSuccess(); } diff --git a/test/fir/loop.fir b/test/fir/loop.fir new file mode 100644 index 000000000000..fabc2caf5b6b --- /dev/null +++ b/test/fir/loop.fir @@ -0,0 +1,11 @@ +func @x(%lb : index, %ub : index, %step : index, %b : i1, %addr : !fir.ref) { + fir.loop %iv = %lb to %ub step %step unordered { + fir.where %b { + fir.store %iv to %addr : !fir.ref + } otherwise { + %zero = constant 0 : index + fir.store %zero to %addr : !fir.ref + } + } + return +} From a55213543185c5100d6cb9f248c028f2405d51b6 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Wed, 20 Nov 2019 15:00:19 -0800 Subject: [PATCH 038/123] name mangling threading --- include/fir/Tilikum/Tilikum.h | 4 +-- lib/fir/Tilikum.cpp | 51 +++++++++++++++++++---------------- tools/bbc/bbc.cc | 8 +++--- tools/tco/tco.cpp | 4 +-- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/include/fir/Tilikum/Tilikum.h b/include/fir/Tilikum/Tilikum.h index 25d8205a491c..a2c8571fdaf5 100644 --- a/include/fir/Tilikum/Tilikum.h +++ b/include/fir/Tilikum/Tilikum.h @@ -29,11 +29,11 @@ namespace fir { struct NameMangler; /// Convert FIR to the LLVM IR dialect -std::unique_ptr createFIRToLLVMPass(); +std::unique_ptr createFIRToLLVMPass(NameMangler &mangler); /// Convert the LLVM IR dialect to LLVM-IR proper std::unique_ptr -createLLVMDialectToLLVMPass(llvm::StringRef output, NameMangler &mangler); +createLLVMDialectToLLVMPass(llvm::StringRef output); } // namespace fir diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index fe517f559513..b3812b0e5b05 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -78,8 +78,8 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { static L::StringMap identStructCache; public: - FIRToLLVMTypeConverter(M::MLIRContext *context) - : LLVMTypeConverter(context), kindMapping(context) {} + FIRToLLVMTypeConverter(M::MLIRContext *context, NameMangler &mangler) + : LLVMTypeConverter(context), kindMapping(context), mangler(mangler) {} // This returns the type of a single column. Rows are added by the caller. // fir.dims --> llvm<"[r x [3 x i64]]"> @@ -310,6 +310,8 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { "conversion resulted in a non-LLVM type"); return wrappedLLVMType; } + + NameMangler &mangler; }; // instantiate static data member @@ -721,7 +723,6 @@ struct ConstantOpConversion : public FIROpConversion { auto attr = constop.getValue(); auto attr_ = attr.cast(); if (auto ft = ty_.dyn_cast()) { - auto kind = ft.getFKind(); L::APFloat f{L::APFloat::IEEEdouble(), attr_.getValue()}; attr = M::FloatAttr::get(M::FloatType::getF64(constop.getContext()), f); } @@ -1902,12 +1903,14 @@ struct NegcOpConversion : public FIROpConversion { /// This pass lowers all FIR dialect operations to LLVM IR dialect. An /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. struct FIRToLLVMLoweringPass : public M::ModulePass { + FIRToLLVMLoweringPass(NameMangler &mangler) : mangler{mangler} {} + void runOnModule() override { if (ClDisableFirToLLVMIR) return; - auto &context{getContext()}; - FIRToLLVMTypeConverter typeConverter{&context}; + auto *context{&getContext()}; + FIRToLLVMTypeConverter typeConverter{context, mangler}; M::OwningRewritePatternList patterns; patterns.insert< AddcOpConversion, AddfOpConversion, AddrOfOpConversion, @@ -1929,9 +1932,9 @@ struct FIRToLLVMLoweringPass : public M::ModulePass { SelectTypeOpConversion, StoreOpConversion, SubcOpConversion, SubfOpConversion, UnboxCharOpConversion, UnboxOpConversion, UnboxProcOpConversion, UndefOpConversion, UnreachableOpConversion>( - &context, typeConverter); + context, typeConverter); M::populateStdToLLVMConversionPatterns(typeConverter, patterns); - M::ConversionTarget target{context}; + M::ConversionTarget target{*context}; target.addLegalDialect(); // required NOP stubs for applying a full conversion @@ -1940,27 +1943,35 @@ struct FIRToLLVMLoweringPass : public M::ModulePass { target.addDynamicallyLegalOp( [&](M::ModuleTerminatorOp) { return true; }); + genDispatchTableMap(); + // apply the patterns if (M::failed(M::applyFullConversion( getModule(), target, std::move(patterns), &typeConverter))) { - M::emitError(M::UnknownLoc::get(&context), + M::emitError(M::UnknownLoc::get(context), "error in converting to LLVM-IR dialect\n"); signalPassFailure(); } } + +private: + void genDispatchTableMap() { + for (auto dt : getModule().getOps()) { + // xxx + } + } + + NameMangler &mangler; }; /// Lower from LLVM IR dialect to proper LLVM-IR and dump the module struct LLVMIRLoweringPass : public M::ModulePass { - LLVMIRLoweringPass(L::StringRef outputName, NameMangler &mangler) - : outputName{outputName}, mangler{mangler} {} + LLVMIRLoweringPass(L::StringRef outputName) : outputName{outputName} {} void runOnModule() override { if (ClDisableLLVM) return; - genDispatchTableMap(); - if (auto llvmModule{M::translateModuleToLLVMIR(getModule())}) { std::error_code ec; L::raw_fd_ostream stream(outputName, ec, L::sys::fs::F_None); @@ -1975,22 +1986,16 @@ struct LLVMIRLoweringPass : public M::ModulePass { } private: - void genDispatchTableMap() { - // TODO - } - L::StringRef outputName; - NameMangler &mangler; }; } // namespace -std::unique_ptr fir::createFIRToLLVMPass() { - return std::make_unique(); +std::unique_ptr +fir::createFIRToLLVMPass(fir::NameMangler &nameMangler) { + return std::make_unique(nameMangler); } -std::unique_ptr -fir::createLLVMDialectToLLVMPass(L::StringRef output, - fir::NameMangler &nameMangler) { - return std::make_unique(output, nameMangler); +std::unique_ptr fir::createLLVMDialectToLLVMPass(L::StringRef output) { + return std::make_unique(output); } diff --git a/tools/bbc/bbc.cc b/tools/bbc/bbc.cc index 4ac7de37e0f3..b4047529f7f9 100644 --- a/tools/bbc/bbc.cc +++ b/tools/bbc/bbc.cc @@ -269,8 +269,8 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, // MLIR+FIR fir::NameMangler nameMangler; - auto burnside = Br::getBurnsideBridge( - semanticsContext.defaultKinds(), &parsing.cooked()); + auto burnside = + Br::getBurnsideBridge(semanticsContext.defaultKinds(), &parsing.cooked()); Br::crossBurnsideBridge(burnside, parseTree, nameMangler); mlir::ModuleOp mlirModule{burnside.getModule()}; mlir::PassManager pm{mlirModule.getContext()}; @@ -285,10 +285,10 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, pm.addPass(fir::createFIRToStdPass()); } if (driver.lowerToLLVM) { - pm.addPass(fir::createFIRToLLVMPass()); + pm.addPass(fir::createFIRToLLVMPass(nameMangler)); } if (driver.lowerToLLVMIR) { - pm.addPass(fir::createLLVMDialectToLLVMPass("a.ll", nameMangler)); + pm.addPass(fir::createLLVMDialectToLLVMPass("a.ll")); } if (mlir::succeeded(pm.run(mlirModule))) { if (driver.dumpFIR) { diff --git a/tools/tco/tco.cpp b/tools/tco/tco.cpp index 1ce63241f2e0..dbb8bbbe7709 100644 --- a/tools/tco/tco.cpp +++ b/tools/tco/tco.cpp @@ -78,8 +78,8 @@ int compileFIR() { pm.addPass(fir::createFIRToStdPass()); // convert loop dialect to standard pm.addPass(mlir::createLowerToCFGPass()); - pm.addPass(fir::createFIRToLLVMPass()); - pm.addPass(fir::createLLVMDialectToLLVMPass(ClOutput, mangler)); + pm.addPass(fir::createFIRToLLVMPass(mangler)); + pm.addPass(fir::createLLVMDialectToLLVMPass(ClOutput)); if (mlir::succeeded(pm.run(*owningRef))) { errs() << ";== output ==\n"; owningRef->dump(); From 7bf6478d355314e63ca6909f7b7e7879e002082a Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 21 Nov 2019 17:47:32 -0800 Subject: [PATCH 039/123] build thin ast --- lib/burnside/CMakeLists.txt | 1 + lib/burnside/ast-builder.cc | 445 ++++++++++++++++++++++++++++++++++++ lib/burnside/ast-builder.h | 250 ++++++++++++++++++++ lib/burnside/flattened.cc | 26 --- 4 files changed, 696 insertions(+), 26 deletions(-) create mode 100644 lib/burnside/ast-builder.cc create mode 100644 lib/burnside/ast-builder.h diff --git a/lib/burnside/CMakeLists.txt b/lib/burnside/CMakeLists.txt index 9a7e83937775..1d9ef68e3af7 100644 --- a/lib/burnside/CMakeLists.txt +++ b/lib/burnside/CMakeLists.txt @@ -15,6 +15,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") add_library(FortranBurnside + ast-builder.cc bridge.cc builder.cc convert-expr.cc diff --git a/lib/burnside/ast-builder.cc b/lib/burnside/ast-builder.cc new file mode 100644 index 000000000000..f222622f4299 --- /dev/null +++ b/lib/burnside/ast-builder.cc @@ -0,0 +1,445 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ast-builder.h" +#include "../parser/parse-tree-visitor.h" +#include +#include + +/// Build an light-weight AST to help with lowering to FIR. The AST will +/// capture pointers back into the parse tree, so the parse tree data structure +/// may not be changed between the construction of the AST and all of +/// its uses. +/// +/// The AST captures a structured view of the program. The program is a list of +/// units. Function like units will contain lists of evaluations. Evaluations +/// are either statements or constructs, where a construct contains a list of +/// evaluations. The resulting AST structure can then be used to create FIR. + +namespace Br = Fortran::burnside; +namespace Co = Fortran::common; +namespace Pa = Fortran::parser; + +using namespace Fortran; +using namespace Br; + +namespace { + +/// The instantiation of a parse tree visitor (Pre and Post) is extremely +/// expensive in terms of compile and link time, so one goal here is to limit +/// the bridge to one such instantiation. +class ASTBuilder { +public: + ASTBuilder() = default; + + /// Get the result + AST::Program result() { return pgm; } + + template constexpr bool Pre(const A &) { return true; } + template constexpr void Post(const A &) {} + + // Module like + + bool Pre(const Pa::Module &x) { return enterModule(x); } + bool Pre(const Pa::Submodule &x) { return enterModule(x); } + + void Post(const Pa::Module &) { exitModule(); } + void Post(const Pa::Submodule &) { exitModule(); } + + // Function like + + bool Pre(const Pa::MainProgram &x) { return enterFunc(x); } + bool Pre(const Pa::FunctionSubprogram &x) { return enterFunc(x); } + bool Pre(const Pa::SubroutineSubprogram &x) { return enterFunc(x); } + bool Pre(const Pa::SeparateModuleSubprogram &x) { return enterFunc(x); } + + void Post(const Pa::MainProgram &) { exitFunc(); } + void Post(const Pa::FunctionSubprogram &) { exitFunc(); } + void Post(const Pa::SubroutineSubprogram &) { exitFunc(); } + void Post(const Pa::SeparateModuleSubprogram &) { exitFunc(); } + + // Block data + + void Post(const Pa::BlockData &x) { + AST::BlockDataUnit unit{x}; + addUnit(unit); + } + + // Evaluation + + void Post(const Pa::Statement &s) { + addEval(makeEvalAction(s)); + } + void Post(const Pa::Statement> &s) { + addEval(makeEvalIndirect(s)); + } + void Post(const Pa::Statement> &s) { + addEval(makeEvalIndirect(s)); + } + void Post(const Pa::Statement> &s) { + addEval(makeEvalIndirect(s)); + } + void Post(const Pa::Statement> &s) { + addEval(makeEvalIndirect(s)); + } + + bool Pre(const Pa::AssociateConstruct &c) { return enterConstruct(c); } + bool Pre(const Pa::BlockConstruct &c) { return enterConstruct(c); } + bool Pre(const Pa::CaseConstruct &c) { return enterConstruct(c); } + bool Pre(const Pa::ChangeTeamConstruct &c) { return enterConstruct(c); } + bool Pre(const Pa::CriticalConstruct &c) { return enterConstruct(c); } + bool Pre(const Pa::DoConstruct &c) { return enterConstruct(c); } + bool Pre(const Pa::IfConstruct &c) { return enterConstruct(c); } + bool Pre(const Pa::SelectRankConstruct &c) { return enterConstruct(c); } + bool Pre(const Pa::SelectTypeConstruct &c) { return enterConstruct(c); } + bool Pre(const Pa::WhereConstruct &c) { return enterConstruct(c); } + bool Pre(const Pa::ForallConstruct &c) { return enterConstruct(c); } + bool Pre(const Pa::CompilerDirective &c) { return enterConstruct(c); } + bool Pre(const Pa::OpenMPConstruct &c) { return enterConstruct(c); } + bool Pre(const Pa::OmpEndLoopDirective &c) { return enterConstruct(c); } + + void Post(const Pa::AssociateConstruct &) { exitConstruct(); } + void Post(const Pa::BlockConstruct &) { exitConstruct(); } + void Post(const Pa::CaseConstruct &) { exitConstruct(); } + void Post(const Pa::ChangeTeamConstruct &) { exitConstruct(); } + void Post(const Pa::CriticalConstruct &) { exitConstruct(); } + void Post(const Pa::DoConstruct &) { exitConstruct(); } + void Post(const Pa::IfConstruct &) { exitConstruct(); } + void Post(const Pa::SelectRankConstruct &) { exitConstruct(); } + void Post(const Pa::SelectTypeConstruct &) { exitConstruct(); } + void Post(const Pa::WhereConstruct &) { exitConstruct(); } + void Post(const Pa::ForallConstruct &) { exitConstruct(); } + void Post(const Pa::CompilerDirective &) { exitConstruct(); } + void Post(const Pa::OpenMPConstruct &) { exitConstruct(); } + void Post(const Pa::OmpEndLoopDirective &) { exitConstruct(); } + +private: + // ActionStmt has a couple of non-conforming cases, which get handled + // explicitly here. The other cases use an Indirection, which we discard in + // the AST. + AST::Evaluation makeEvalAction(const Pa::Statement &s) { + return std::visit( + common::visitors{ + [&](const Pa::ContinueStmt &x) { + return AST::Evaluation{x, s.source, s.label}; + }, + [&](const Pa::FailImageStmt &x) { + return AST::Evaluation{x, s.source, s.label}; + }, + [&](const auto &x) { + return AST::Evaluation{x.value(), s.source, s.label}; + }, + }, + s.statement.u); + } + + template + AST::Evaluation makeEvalIndirect(const Pa::Statement> &s) { + return AST::Evaluation{s.statement.value(), s.source, s.label}; + } + + // When we enter a function-like structure, we want to build a new unit and + // set the builder's cursors to point to it. + template bool enterFunc(const A &f) { + AST::FunctionLikeUnit unit{f}; + funclist = &unit.funcs; + pushEval(&unit.evals); + addFunc(unit); + return true; + } + + void exitFunc() { + funclist = nullptr; + popEval(); + } + + // When we enter a construct structure, we want to build a new construct and + // set the builder's evaluation cursor to point to it. + template bool enterConstruct(const A &c) { + AST::Evaluation con{c}; + addEval(con); + pushEval(con.getConstructEvals()); + return true; + } + + void exitConstruct() { popEval(); } + + // When we enter a module structure, we want to build a new module and + // set the builder's function cursor to point to it. + template bool enterModule(const A &f) { + AST::ModuleLikeUnit unit{f}; + funclist = &unit.funcs; + addUnit(unit); + return true; + } + + void exitModule() { funclist = nullptr; } + + template void addUnit(const A &unit) { + pgm.units.emplace_back(unit); + } + + template void addFunc(const A &func) { + if (funclist) + funclist->emplace_back(func); + else + addUnit(func); + } + + void addEval(const AST::Evaluation &eval) { + assert(funclist); + evallist.back()->emplace_back(eval); + } + + void pushEval(std::list *eval) { + assert(funclist); + evallist.push_back(eval); + } + + void popEval() { + assert(funclist); + evallist.pop_back(); + } + + AST::Program pgm; + std::list *funclist{nullptr}; + std::vector *> evallist; +}; + +template constexpr bool hasErrLabel(const A &stmt) { + if constexpr (std::is_same_v || + std::is_same_v) { + for (const auto &control : stmt.controls) { + if (std::holds_alternative(control.u)) { + return true; + } + } + } + if constexpr (std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v) { + for (const auto &spec : stmt.v) { + if (std::holds_alternative(spec.u)) { + return true; + } + } + } + if constexpr (std::is_same_v) { + for (const auto &spec : std::get>(stmt.u)) { + if (std::holds_alternative(spec.u)) { + return true; + } + } + } + return false; +} + +template constexpr bool hasEorLabel(const A &stmt) { + if constexpr (std::is_same_v || + std::is_same_v) { + for (const auto &control : stmt.controls) { + if (std::holds_alternative(control.u)) { + return true; + } + } + } + if constexpr (std::is_same_v) { + for (const auto &waitSpec : stmt.v) { + if (std::holds_alternative(waitSpec.u)) { + return true; + } + } + } + return false; +} + +template constexpr bool hasEndLabel(const A &stmt) { + if constexpr (std::is_same_v || + std::is_same_v) { + for (const auto &control : stmt.controls) { + if (std::holds_alternative(control.u)) { + return true; + } + } + } + if constexpr (std::is_same_v) { + for (const auto &waitSpec : stmt.v) { + if (std::holds_alternative(waitSpec.u)) { + return true; + } + } + } + return false; +} + +bool hasAltReturns(const Pa::CallStmt &callStmt) { + const auto &args{std::get>(callStmt.v.t)}; + for (const auto &arg : args) { + const auto &actual{std::get(arg.t)}; + if (std::holds_alternative(actual.u)) { + return true; + } + } + return false; +} + +/// Determine if `callStmt` has alternate returns and if so set `e` to be the +/// origin of a switch-like control flow +void altRet( + AST::Evaluation &e, const Pa::CallStmt *callStmt, AST::Evaluation *cstr) { + if (hasAltReturns(*callStmt)) { + e.setCFG(AST::CFGAnnotation::Switch, cstr); + } +} + +template +void ioLabel(AST::Evaluation &e, const A *s, AST::Evaluation *cstr) { + if (hasErrLabel(*s) || hasEorLabel(*s) || hasEndLabel(*s)) { + e.setCFG(AST::CFGAnnotation::IoSwitch, cstr); + } +} + +void annotateEvalListCFG( + std::list &evals, AST::Evaluation *cstr) { + bool nextIsTarget = false; + for (auto e : evals) { + e.isTarget = nextIsTarget; + nextIsTarget = false; + if (e.isConstruct()) { + annotateEvalListCFG(*e.getConstructEvals(), &e); + // assume that the entry and exit are both possible branch targets + e.isTarget = nextIsTarget = true; + continue; + } + std::visit( + common::visitors{ + [&](Pa::BackspaceStmt *s) { ioLabel(e, s, cstr); }, + [&](Pa::CallStmt *s) { altRet(e, s, cstr); }, + [&](Pa::CloseStmt *s) { ioLabel(e, s, cstr); }, + [&](Pa::CycleStmt *) { e.setCFG(AST::CFGAnnotation::Goto, cstr); }, + [&](Pa::EndfileStmt *s) { ioLabel(e, s, cstr); }, + [&](Pa::ExitStmt *) { e.setCFG(AST::CFGAnnotation::Goto, cstr); }, + [&](Pa::FailImageStmt *) { + e.setCFG(AST::CFGAnnotation::Return, cstr); + }, + [&](Pa::FlushStmt *s) { ioLabel(e, s, cstr); }, + [&](Pa::GotoStmt *) { e.setCFG(AST::CFGAnnotation::Goto, cstr); }, + [&](Pa::IfStmt *) { e.setCFG(AST::CFGAnnotation::CondGoto, cstr); }, + [&](Pa::InquireStmt *s) { ioLabel(e, s, cstr); }, + [&](Pa::OpenStmt *s) { ioLabel(e, s, cstr); }, + [&](Pa::ReadStmt *s) { ioLabel(e, s, cstr); }, + [&](Pa::ReturnStmt *) { + e.setCFG(AST::CFGAnnotation::Return, cstr); + }, + [&](Pa::RewindStmt *s) { ioLabel(e, s, cstr); }, + [&](Pa::StopStmt *) { e.setCFG(AST::CFGAnnotation::Return, cstr); }, + [&](Pa::WaitStmt *s) { ioLabel(e, s, cstr); }, + [&](Pa::WriteStmt *s) { ioLabel(e, s, cstr); }, + [&](Pa::ArithmeticIfStmt *) { + e.setCFG(AST::CFGAnnotation::Switch, cstr); + }, + [&](Pa::AssignedGotoStmt *) { + e.setCFG(AST::CFGAnnotation::IndGoto, cstr); + }, + [&](Pa::ComputedGotoStmt *) { + e.setCFG(AST::CFGAnnotation::Switch, cstr); + }, + [](auto *) { /* do nothing */ }, + }, + e.u); + if (e.isActionStmt && + std::get>( + std::get(e.ux)) + .has_value()) { + e.isTarget = true; + } + } +} + +inline void annotateFuncCFG(AST::FunctionLikeUnit &flu) { + annotateEvalListCFG(flu.evals, nullptr); +} + +} // namespace + +Br::AST::FunctionLikeUnit::FunctionLikeUnit(const Pa::MainProgram &f) + : ProgramUnit{&f} { + auto &ps{std::get>>(f.t)}; + if (ps.has_value()) { + const Pa::Statement &s{ps.value()}; + funStmts.push_back(&s); + } + funStmts.push_back(&std::get>(f.t)); +} + +Br::AST::FunctionLikeUnit::FunctionLikeUnit(const Pa::FunctionSubprogram &f) + : ProgramUnit{&f} { + funStmts.push_back(&std::get>(f.t)); + funStmts.push_back(&std::get>(f.t)); +} + +Br::AST::FunctionLikeUnit::FunctionLikeUnit(const Pa::SubroutineSubprogram &f) + : ProgramUnit{&f} { + funStmts.push_back(&std::get>(f.t)); + funStmts.push_back(&std::get>(f.t)); +} + +Br::AST::FunctionLikeUnit::FunctionLikeUnit( + const Pa::SeparateModuleSubprogram &f) + : ProgramUnit{&f} { + funStmts.push_back(&std::get>(f.t)); + funStmts.push_back(&std::get>(f.t)); +} + +Br::AST::ModuleLikeUnit::ModuleLikeUnit(const Pa::Module &m) : ProgramUnit{&m} { + modStmts.push_back(&std::get>(m.t)); + modStmts.push_back(&std::get>(m.t)); +} + +Br::AST::ModuleLikeUnit::ModuleLikeUnit(const Pa::Submodule &m) + : ProgramUnit{&m} { + modStmts.push_back(&std::get>(m.t)); + modStmts.push_back(&std::get>(m.t)); +} + +Br::AST::BlockDataUnit::BlockDataUnit(const Pa::BlockData &db) + : ProgramUnit{&db} {} + +AST::Program Br::createAST(const Pa::Program &root) { + ASTBuilder walker; + Walk(root, walker); + return walker.result(); +} + +void Br::annotateControl(AST::Program &ast) { + for (auto unit : ast.units) { + std::visit(common::visitors{ + [](AST::BlockDataUnit &) {}, + [](AST::FunctionLikeUnit &f) { + annotateFuncCFG(f); + for (auto s : f.funcs) { + annotateFuncCFG(s); + } + }, + [](AST::ModuleLikeUnit &u) { + for (auto f : u.funcs) { + annotateFuncCFG(f); + } + }, + }, + unit); + } +} diff --git a/lib/burnside/ast-builder.h b/lib/burnside/ast-builder.h new file mode 100644 index 000000000000..189f5b212729 --- /dev/null +++ b/lib/burnside/ast-builder.h @@ -0,0 +1,250 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_AST_BUILDER_H_ +#define FORTRAN_BURNSIDE_AST_BUILDER_H_ + +#include "../parser/parse-tree.h" +#include "../semantics/scope.h" + +namespace Fortran::burnside { +namespace AST { + +enum class CFGAnnotation { + None, + Goto, + CondGoto, + IndGoto, + IoSwitch, + Switch, + Return +}; + +/// Function-like units can contains lists of evaluations. These can be +/// (simple) statements or constructs, where a construct contains its own +/// evaluations. +struct Evaluation { + using EvalVariant = std::variant; + using StmtExtra = std::tuple>; + + Evaluation() = delete; + + /// Statement ctor + template + Evaluation(const A &a, const parser::CharBlock &pos, + const std::optional &lab) + : u{&a}, ux{StmtExtra{pos, lab}} { + static_assert(!isConstruct(a) && "must be a statement"); + if constexpr (isAction(a)) { + isActionStmt = true; + } + if constexpr (isAction(a) || isOther(a)) { + isStatement = true; + } + } + + /// Construct ctor + template + Evaluation(const A &a) : u{&a}, ux{std::list{}} { + static_assert(isConstruct(a) && "must be a construct"); + } + + /// statements that are executable (actions) + template constexpr static bool isAction(const A &a) { + if constexpr (!isConstruct(a) && !isOther(a)) { + return true; + } else { + return false; + } + } + + /// constructs (and directives) + template constexpr static bool isConstruct(const A &) { + if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) { + return true; + } else { + return false; + } + } + + /// statements that are not executable + template constexpr static bool isOther(const A &) { + if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) { + return true; + } else { + return false; + } + } + + constexpr bool isStmt() const { return isStatement; } + constexpr bool isConstruct() const { return !isStmt(); } + + /// Set the type of originating control flow type for this evaluation. + void setCFG(CFGAnnotation a, Evaluation *cstr) { + cfg = a; + setBranches(cstr); + } + + /// Is this evaluation a control-flow origin? (The AST must be annotated) + bool isControlOrigin() const { return cfg != CFGAnnotation::None; } + + /// Is this evaluation a control-flow target? (The AST must be annotated) + bool isControlTarget() const { return isTarget; } + + /// Set the hasBranches flag iff this evaluation (a construct) contains + /// control flow + void setBranches() { hasBranches = true; } + + constexpr std::list *getConstructEvals() { + return isStatement ? nullptr : std::get_if>(&ux); + } + + /// Set that the construct `cstr` (if not a nullptr) has branches. + static void setBranches(Evaluation *cstr) { + if (cstr) { + cstr->setBranches(); + } + } + + EvalVariant u; + std::variant> ux; + CFGAnnotation cfg{CFGAnnotation::None}; + bool isStatement{false}; + bool isActionStmt{false}; + bool isTarget{false}; + bool hasBranches{false}; +}; + +/// A program is a list of program units. +/// These units can be function like, module like, or block data +struct ProgramUnit { + template ProgramUnit(A *ptr) : p{ptr} {} + + std::variant + p; +}; + +/// Function-like units have similar structure. They all can contain executable +/// statements. +struct FunctionLikeUnit : public ProgramUnit { + // wrapper statements for function-like syntactic structures + using FunctionStatement = + std::variant *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *>; + + FunctionLikeUnit(const parser::MainProgram &f); + FunctionLikeUnit(const parser::FunctionSubprogram &f); + FunctionLikeUnit(const parser::SubroutineSubprogram &f); + FunctionLikeUnit(const parser::SeparateModuleSubprogram &f); + + const semantics::Scope *scope{nullptr}; + std::list funStmts; + std::list evals; + std::list funcs; +}; + +/// Module-like units have similar structure. They all can contain a list of +/// function-like units. +struct ModuleLikeUnit : public ProgramUnit { + // wrapper statements for module-like syntactic structures + using ModuleStatement = + std::variant *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *>; + + ModuleLikeUnit(const parser::Module &m); + ModuleLikeUnit(const parser::Submodule &m); + ~ModuleLikeUnit() = default; + + const semantics::Scope *scope{nullptr}; + std::list modStmts; + std::list funcs; +}; + +struct BlockDataUnit : public ProgramUnit { + BlockDataUnit(const parser::BlockData &db); +}; + +/// A Program is the top-level AST +struct Program { + using Units = std::variant; + std::list units; +}; + +} // namespace AST + +/// Create an AST from the parse tree +AST::Program createAST(const parser::Program &root); + +/// Decorate the AST with control flow annotations +void annotateControl(AST::Program &ast); + +} // namespace burnside + +#endif // FORTRAN_BURNSIDE_AST_BUILDER_H_ diff --git a/lib/burnside/flattened.cc b/lib/burnside/flattened.cc index 0a5f6c91e9b2..54c0a9e8cc01 100644 --- a/lib/burnside/flattened.cc +++ b/lib/burnside/flattened.cc @@ -277,26 +277,6 @@ template std::string GetSource(const B *s) { return GetSource(&std::get>(s->t)); } -static bool meetsBlockConstraints(const parser::Block &block) { return false; } - -static bool meetsLoopConstraints(const parser::DoConstruct &loop) { - return meetsBlockConstraints(std::get(loop.t)); -} - -static bool meetsWhereConstraints(const parser::IfConstruct &w) { - // we can do better, but for now don't allow ELSE IF constructs - if (std::get>(w.t).empty()) { - const auto &optElse{ - std::get>(w.t)}; - if (optElse.has_value() && - !meetsBlockConstraints(std::get(optElse->t))) { - return false; - } - return meetsBlockConstraints(std::get(w.t)); - } - return false; -} - void Op::Build(std::list &ops, const parser::Statement &ec, AnalysisData &ad) { std::visit( @@ -504,9 +484,6 @@ struct ControlFlowAnalyzer { /// `DO` constructs can be lowered to `fir.loop` if they meet some /// constraints, otherwise they are lowered to a CFG. bool Pre(const parser::DoConstruct &construct) { - if (meetsLoopConstraints(construct)) { - // use `fir.loop` - } std::list ops; LabelOp backedgeLab{buildNewLabel()}; LabelOp incrementLab{buildNewLabel()}; @@ -543,9 +520,6 @@ struct ControlFlowAnalyzer { /// `IF` constructs can be lowered to `fir.where` if they meet some /// constraints, otherwise they are lowered to a CFG. bool Pre(const parser::IfConstruct &construct) { - if (meetsWhereConstraints(construct)) { - // use `fir.where` - } std::list ops; LabelOp thenLab{buildNewLabel()}; LabelOp elseLab{buildNewLabel()}; From e4330e8a687f1bc0c08e1a4852a2f01c32c51b82 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Mon, 25 Nov 2019 14:38:51 -0800 Subject: [PATCH 040/123] interface cleanup --- lib/burnside/bridge.cc | 16 +++------------- lib/burnside/bridge.h | 15 +++------------ tools/bbc/bbc.cc | 6 +++--- 3 files changed, 9 insertions(+), 28 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index b79bcd3831ba..54853c7c21a5 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -1027,16 +1027,12 @@ void FIRConverter::translateRoutine( } // namespace -void Br::crossBurnsideBridge( - BurnsideBridge &bridge, const Pa::Program &prg, fir::NameMangler &mangler) { - FIRConverter converter{bridge}; +void Br::BurnsideBridge::lower( + const Pa::Program &prg, fir::NameMangler &mangler) { + FIRConverter converter{*this}; Walk(prg, converter); } -std::unique_ptr Br::LLVMBridge(M::ModuleOp &module) { - return M::translateModuleToLLVMIR(module); -} - void Br::BurnsideBridge::parseSourceFile(llvm::SourceMgr &srcMgr) { auto owningRef = M::parseSourceFile(srcMgr, context.get()); module.reset(new M::ModuleOp(owningRef.get().getOperation())); @@ -1051,9 +1047,3 @@ Br::BurnsideBridge::BurnsideBridge( module = std::make_unique( M::ModuleOp::create(M::UnknownLoc::get(context.get()))); } - -BurnsideBridge Br::getBurnsideBridge( - const Co::IntrinsicTypeDefaultKinds &defaultKinds, - const Pa::CookedSource *cooked) { - return BurnsideBridge::create(defaultKinds, cooked); -} diff --git a/lib/burnside/bridge.h b/lib/burnside/bridge.h index 00955159f8f0..58207be1a542 100644 --- a/lib/burnside/bridge.h +++ b/lib/burnside/bridge.h @@ -65,6 +65,9 @@ class BurnsideBridge { const parser::CookedSource *getCookedSource() const { return cooked; } + /// Cross the bridge from the Fortran parse-tree, etc. to FIR+OpenMP+MLIR + void lower(const parser::Program &program, fir::NameMangler &mangler); + private: explicit BurnsideBridge(const common::IntrinsicTypeDefaultKinds &defaultKinds, const parser::CookedSource *cooked); @@ -77,18 +80,6 @@ class BurnsideBridge { std::unique_ptr module; }; -/// Cross the bridge from the Fortran parse-tree, etc. to FIR+OpenMP+MLIR -void crossBurnsideBridge(BurnsideBridge &bridge, const parser::Program &program, - fir::NameMangler &mangler); - -/// Bridge from MLIR to LLVM-IR -std::unique_ptr LLVMBridge(mlir::ModuleOp &module); - -/// Get an instance of the Burnside bridge -BurnsideBridge getBurnsideBridge( - const common::IntrinsicTypeDefaultKinds &defaultKinds, - const parser::CookedSource *cooked = nullptr); - } // Fortran::burnside #endif // FORTRAN_BURNSIDE_BRIDGE_H_ diff --git a/tools/bbc/bbc.cc b/tools/bbc/bbc.cc index b4047529f7f9..cbe64733df58 100644 --- a/tools/bbc/bbc.cc +++ b/tools/bbc/bbc.cc @@ -269,9 +269,9 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, // MLIR+FIR fir::NameMangler nameMangler; - auto burnside = - Br::getBurnsideBridge(semanticsContext.defaultKinds(), &parsing.cooked()); - Br::crossBurnsideBridge(burnside, parseTree, nameMangler); + auto burnside = Br::BurnsideBridge::create( + semanticsContext.defaultKinds(), &parsing.cooked()); + burnside.lower(parseTree, nameMangler); mlir::ModuleOp mlirModule{burnside.getModule()}; mlir::PassManager pm{mlirModule.getContext()}; if (driver.dumpHLFIR) { From c5220ddf778a7058ea58c6ad0f84de8375cbe335 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 27 Nov 2019 16:13:43 -0800 Subject: [PATCH 041/123] changes --- lib/fir/Tilikum.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index b3812b0e5b05..f97f1ca00bca 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -1957,7 +1957,8 @@ struct FIRToLLVMLoweringPass : public M::ModulePass { private: void genDispatchTableMap() { for (auto dt : getModule().getOps()) { - // xxx + // FIXME + (void)dt; } } From ab016e6b7145cd71f27fb83c3eea4b3e3cfc3e25 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Wed, 20 Nov 2019 05:45:00 -0800 Subject: [PATCH 042/123] Propagate logicals in the type required by consumers: Inside the `ExprLowering` facility: - After evaluating an `Expr>` expression, convert the result to the type requested by the consumer of the expression if needed. - A state `genLogicalAsI1` is added to `ExprLowering` facility. It allows to propagate the final consumer type through nodes where the type manipulated does not matter (e.g. parantheses) while also allowing a node of a tree to locally change the requested type to produce its logical operands. At the external interface of `ExprLowering` facility: - Add `createI1LogicalExpression` external methods that can be used to the bridge. - The default expression generation `createSomeExpression` generate `fir.logical` which is what is needed in assignements and calls as well as in weird intrinsics (TRANSFER) where the physical representation matters. This design fulfills: - It will be possible to lower `TRANSFER(TRANSFER(int, logical))` in such as it still evaluate to `int` as requested by 16.9.193. - It avoids generating useless conversions in the middle of logical operations such as `if(x.le.y.AND.x.gt.z) then ...` - It has a "low" footprint on the rest of the bridge. Parts that handle logical expressions without caring about what it is do not have to add code to handle the FIR/MLIR type possibilities. Also addresses https://github.com/schweitzpgi/f18/pull/16 comments - Add assert message for `.NOT.`. - Move `getMLIRLogicaType` to `convert-type.h`. --- lib/burnside/bridge.cc | 9 ++- lib/burnside/convert-expr.cc | 122 +++++++++++++++++++++-------------- lib/burnside/convert-expr.h | 6 ++ lib/burnside/convert-type.cc | 4 ++ lib/burnside/convert-type.h | 4 +- 5 files changed, 92 insertions(+), 53 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 54853c7c21a5..f571f9985f0f 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -117,6 +117,10 @@ class FIRConverter { return createSomeExpression( loc, build(), *expr, symbolMap, defaults, intrinsics); } + M::Value *createLogicalExprAsI1(M::Location loc, const SomeExpr *expr) { + return createI1LogicalExpression( + loc, build(), *expr, symbolMap, defaults, intrinsics); + } M::Value *createTemp(M::Type type, Se::Symbol *symbol = nullptr) { return createTemporary(toLocation(), build(), symbolMap, type, symbol); } @@ -516,7 +520,7 @@ class FIRConverter { const A &tuple, Fl::LabelMention trueLabel, Fl::LabelMention falseLabel) { auto *exprRef{Se::GetExpr(std::get(tuple))}; assert(exprRef && "condition expression missing"); - auto *cond{createFIRExpr(toLocation(), exprRef)}; + auto *cond{createLogicalExprAsI1(toLocation(), exprRef)}; genCondBranch(cond, trueLabel, falseLabel); } void genFIR(const Pa::Statement &stmt, @@ -561,7 +565,7 @@ class FIRConverter { [&](const parser::ScalarLogicalExpr &logical) { auto loc{toLocation(stmt.source)}; auto *exp{Se::GetExpr(logical)}; - condition = createFIRExpr(loc, exp); + condition = createLogicalExprAsI1(loc, exp); return false; }, [&](const parser::LoopControl::Concurrent &concurrent) { @@ -583,6 +587,7 @@ class FIRConverter { // Action statements void genFIR(const Pa::AllocateStmt &stmt) { TODO(); } + void genFIR(const Pa::AssignmentStmt &stmt) { auto *rhs{Se::GetExpr(std::get(stmt.t))}; auto *lhs{Se::GetExpr(std::get(stmt.t))}; diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 0f2343f5f264..1afe73f46a60 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -73,6 +73,7 @@ class ExprLowering { SymMap loadedSymbols{}; Co::IntrinsicTypeDefaultKinds const &defaults; IntrinsicLibrary const &intrinsics; + bool genLogicalAsI1{false}; M::Location getLoc() { return location; } @@ -117,21 +118,12 @@ class ExprLowering { } /// Generate a logical/boolean constant of `value` - M::Type getMLIRlogicalType() { - return M::IntegerType::get(1, builder.getContext()); - } M::Value *genMLIRLogicalConstant(M::MLIRContext *context, bool value) { - auto attr{builder.getIntegerAttr(getMLIRlogicalType(), value ? 1 : 0)}; - return builder.create(getLoc(), getMLIRlogicalType(), attr) + M::Type mlirLogicalType{getMLIRlogicalType(builder.getContext())}; + auto attr{builder.getIntegerAttr(mlirLogicalType, value ? 1 : 0)}; + return builder.create(getLoc(), mlirLogicalType, attr) .getResult(); } - template - M::Value *genLogicalConstant(M::MLIRContext *context, bool value) { - auto mlirCst{genMLIRLogicalConstant(context, value)}; - M::Type firLogicalTy{getFIRType(context, defaults, LogicalCat, KIND)}; - auto res{builder.create(getLoc(), firLogicalTy, mlirCst)}; - return res.getResult(); - } template M::Value *genRealConstant(M::MLIRContext *context, L::APFloat const &value) { @@ -158,19 +150,6 @@ class ExprLowering { template M::Value *createBinaryOp(A const &ex) { return createBinaryOp(ex, genval(ex.left()), genval(ex.right())); } - template M::Value *createLogicalOp(A const &ex) { - auto mlirTy{M::IntegerType::get(1, builder.getContext())}; - auto *lhs{genval(ex.left())}; - auto *rhs{genval(ex.right())}; - // mlir logical ops do not work with fir.logical, so the operation - // is wrapped in conversions - auto lhsConv{builder.create(getLoc(), mlirTy, lhs)}; - auto rhsConv{builder.create(getLoc(), mlirTy, rhs)}; - auto op{createBinaryOp(ex, lhsConv, rhsConv)}; - assert(lhs); - auto resType{lhs->getType()}; - return builder.create(getLoc(), resType, op); - } M::FuncOp getFunction(L::StringRef name, M::FunctionType funTy) { auto module{getModule(&builder)}; @@ -251,6 +230,7 @@ class ExprLowering { translateSymbolToFIRType(builder.getContext(), defaults, sym), &*sym); } M::Value *gendef(Se::SymbolRef sym) { return gen(sym); } + M::Value *genval(Se::SymbolRef sym) { // Do not load the same symbols several time in one expression. // Fortran guarantees variable value must be the same wherever it @@ -387,11 +367,9 @@ class ExprLowering { static_assert(TC == CharacterCat); TODO(); } - auto logicalTy{getFIRType(builder.getContext(), defaults, LogicalCat)}; - return builder.create(getLoc(), logicalTy, result); + return result; } - // TODO JP: the thing below should not be required. M::Value *genval(Ev::Relational const &op) { return std::visit([&](const auto &x) { return genval(x); }, op.u); } @@ -399,27 +377,34 @@ class ExprLowering { template M::Value *genval(Ev::Convert, TC2> const &convert) { auto ty{getFIRType(builder.getContext(), defaults, TC1, KIND)}; - return builder.create(getLoc(), ty, genval(convert.left())); + M::Value *operand{genval(convert.left())}; + if (TC1 == LogicalCat && genLogicalAsI1) { + // If an i1 result is needed, it does not make sens to convert between + // `fir.logical` types to later convert back to the result to i1. + return operand; + } else { + return builder.create(getLoc(), ty, operand); + } } + template M::Value *genval(Ev::Parentheses const &) { TODO(); } template M::Value *genval(const Ev::Not &op) { + // Request operands to be generated as `i1` and restore after this scope. + auto restorer{common::ScopedSet(genLogicalAsI1, true)}; auto *context{builder.getContext()}; - auto mlirLogical{builder.create( - getLoc(), getMLIRlogicalType(), genval(op.left()))}; - auto i1One{genMLIRLogicalConstant(context, 1)}; - auto mlirRes{builder.create(getLoc(), mlirLogical, i1One)}; - auto firTy{getFIRType(builder.getContext(), defaults, LogicalCat, KIND)}; - return builder.create(getLoc(), firTy, mlirRes).getResult(); + auto logical{genval(op.left())}; + auto one{genMLIRLogicalConstant(context, 1)}; + return builder.create(getLoc(), logical, one).getResult(); } template M::Value *genval(Ev::LogicalOperation const &op) { + // Request operands to be generated as `i1` and restore after this scope. + auto restorer{common::ScopedSet(genLogicalAsI1, true)}; mlir::Value *result{nullptr}; switch (op.logicalOperator) { - case Ev::LogicalOperator::And: - result = createLogicalOp(op); - break; - case Ev::LogicalOperator::Or: result = createLogicalOp(op); break; + case Ev::LogicalOperator::And: result = createBinaryOp(op); break; + case Ev::LogicalOperator::Or: result = createBinaryOp(op); break; case Ev::LogicalOperator::Eqv: result = createCompareOp(op, M::CmpIPredicate::eq); break; @@ -427,16 +412,14 @@ class ExprLowering { result = createCompareOp(op, M::CmpIPredicate::ne); break; case Ev::LogicalOperator::Not: - // LogicalOperations are binary operations. Expr for Not is - // evaluate::Not. - assert(false); + // lib/evaluate expression for .NOT. is evaluate::Not. + assert(false && ".NOT. is not a binary operator"); break; } if (!result) { assert(false && "unhandled logical operation"); } - auto logicalTy{getFIRType(builder.getContext(), defaults, LogicalCat)}; - return builder.create(getLoc(), logicalTy, result); + return result; } template @@ -454,7 +437,7 @@ class ExprLowering { } else if constexpr (TC == LogicalCat) { auto opt{con.GetScalarValue()}; if (opt.has_value()) - return genLogicalConstant(builder.getContext(), opt->IsTrue()); + return genMLIRLogicalConstant(builder.getContext(), opt->IsTrue()); assert(false && "logical constant has no value"); return {}; } else if constexpr (TC == RealCat) { @@ -698,8 +681,7 @@ class ExprLowering { M::FunctionType funTy{ M::FunctionType::get(argTypes, resultType, builder.getContext())}; M::FuncOp func{getFunction(funRef.proc().GetName(), funTy)}; - M::CallOp call{builder.create(getLoc(), func, operands)}; - return call.getResult(0); + return builder.create(getLoc(), func, operands).getResult(0); } } @@ -714,6 +696,38 @@ class ExprLowering { return std::visit([&](const auto &e) { return genval(e); }, exp.u); } + template + M::Value *genval(Ev::Expr> const &exp) { + auto *rawResult{ + std::visit([&](const auto &e) { return genval(e); }, exp.u)}; + // Handle the `i1` to `fir.logical` conversions as needed. + auto *typedResult{rawResult}; + if (rawResult) { + M::Type type{rawResult->getType()}; + if (type.isa()) { + if (genLogicalAsI1) { + M::Type mlirLogicalType{getMLIRlogicalType(builder.getContext())}; + typedResult = builder.create( + getLoc(), mlirLogicalType, rawResult); + } + } else if (type.isa()) { + if (!genLogicalAsI1) { + M::Type firLogicalType{ + getFIRType(builder.getContext(), defaults, LogicalCat, KIND)}; + typedResult = builder.create( + getLoc(), firLogicalType, rawResult); + } + } else if (auto seqType{type.dyn_cast_or_null()}) { + // TODO: Conversions at array level should probably be avoided. + // This depends on how array expressions will be lowered. + assert(false && "logical array loads not yet implemented"); + } else { + assert(false && "unexpected logical type in expression"); + } + } + return typedResult; + } + template M::Value *gendef(const A &) { assert(false && "expression error"); return {}; @@ -723,9 +737,9 @@ class ExprLowering { explicit ExprLowering(M::Location loc, M::OpBuilder &bldr, SomeExpr const &vop, SymMap &map, Co::IntrinsicTypeDefaultKinds const &defaults, - IntrinsicLibrary const &intr) + IntrinsicLibrary const &intr, bool logicalAsI1 = false) : location{loc}, builder{bldr}, expr{vop}, symMap{map}, defaults{defaults}, - intrinsics{intr} {} + intrinsics{intr}, genLogicalAsI1{logicalAsI1} {} /// Lower the expression `expr` into MLIR standard dialect M::Value *gen() { return gen(expr); } @@ -738,7 +752,15 @@ M::Value *Br::createSomeExpression(M::Location loc, M::OpBuilder &builder, Ev::Expr const &expr, SymMap &symMap, Co::IntrinsicTypeDefaultKinds const &defaults, IntrinsicLibrary const &intrinsics) { - return ExprLowering{loc, builder, expr, symMap, defaults, intrinsics} + return ExprLowering{loc, builder, expr, symMap, defaults, intrinsics, false} + .genval(); +} + +M::Value *Br::createI1LogicalExpression(M::Location loc, M::OpBuilder &builder, + Ev::Expr const &expr, SymMap &symMap, + Co::IntrinsicTypeDefaultKinds const &defaults, + IntrinsicLibrary const &intrinsics) { + return ExprLowering{loc, builder, expr, symMap, defaults, intrinsics, true} .genval(); } diff --git a/lib/burnside/convert-expr.h b/lib/burnside/convert-expr.h index ffbea43bce06..7c8b7e9136e4 100644 --- a/lib/burnside/convert-expr.h +++ b/lib/burnside/convert-expr.h @@ -50,6 +50,12 @@ mlir::Value *createSomeExpression(mlir::Location loc, mlir::OpBuilder &builder, evaluate::Expr const &expr, SymMap &symMap, common::IntrinsicTypeDefaultKinds const &defaults, IntrinsicLibrary const &intrinsics); + +mlir::Value *createI1LogicalExpression(mlir::Location loc, + mlir::OpBuilder &builder, evaluate::Expr const &expr, + SymMap &symMap, common::IntrinsicTypeDefaultKinds const &defaults, + IntrinsicLibrary const &intrinsics); + mlir::Value *createSomeAddress(mlir::Location loc, mlir::OpBuilder &builder, evaluate::Expr const &expr, SymMap &symMap, common::IntrinsicTypeDefaultKinds const &defaults, diff --git a/lib/burnside/convert-type.cc b/lib/burnside/convert-type.cc index 1b5cbe5575b3..cdc0eac83013 100644 --- a/lib/burnside/convert-type.cc +++ b/lib/burnside/convert-type.cc @@ -388,3 +388,7 @@ M::Type Br::translateSymbolToFIRType(M::MLIRContext *context, M::Type Br::convertReal(M::MLIRContext *context, int kind) { return genFIRType(context, kind); } + +M::Type Br::getMLIRlogicalType(M::MLIRContext *context) { + return M::IntegerType::get(1, context); +} diff --git a/lib/burnside/convert-type.h b/lib/burnside/convert-type.h index 34218eb87b97..ce7d3ab3a0e9 100644 --- a/lib/burnside/convert-type.h +++ b/lib/burnside/convert-type.h @@ -107,7 +107,9 @@ mlir::Type translateSymbolToFIRType(mlir::MLIRContext *ctxt, common::IntrinsicTypeDefaultKinds const &defaults, const semantics::SymbolRef symbol); -mlir::Type convertReal(mlir::MLIRContext *context, int KIND); +mlir::Type convertReal(mlir::MLIRContext *ctxt, int KIND); + +mlir::Type getMLIRlogicalType(mlir::MLIRContext *ctxt); } // burnside } // Fortran From 3cab1bd24e39aacb159446d65920cda420f1538a Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Wed, 27 Nov 2019 07:48:08 -0800 Subject: [PATCH 043/123] fix constant expression issues --- lib/burnside/ast-builder.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/burnside/ast-builder.h b/lib/burnside/ast-builder.h index 189f5b212729..41d98d2d5902 100644 --- a/lib/burnside/ast-builder.h +++ b/lib/burnside/ast-builder.h @@ -74,11 +74,11 @@ struct Evaluation { Evaluation(const A &a, const parser::CharBlock &pos, const std::optional &lab) : u{&a}, ux{StmtExtra{pos, lab}} { - static_assert(!isConstruct(a) && "must be a statement"); - if constexpr (isAction(a)) { + static_assert(!isConstruct() && "must be a statement"); + if constexpr (isAction()) { isActionStmt = true; } - if constexpr (isAction(a) || isOther(a)) { + if constexpr (isAction() || isOther()) { isStatement = true; } } @@ -86,12 +86,12 @@ struct Evaluation { /// Construct ctor template Evaluation(const A &a) : u{&a}, ux{std::list{}} { - static_assert(isConstruct(a) && "must be a construct"); + static_assert(isConstruct() && "must be a construct"); } /// statements that are executable (actions) - template constexpr static bool isAction(const A &a) { - if constexpr (!isConstruct(a) && !isOther(a)) { + template constexpr static bool isAction() { + if constexpr (!isConstruct() && !isOther()) { return true; } else { return false; @@ -99,7 +99,7 @@ struct Evaluation { } /// constructs (and directives) - template constexpr static bool isConstruct(const A &) { + template constexpr static bool isConstruct() { if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || @@ -121,7 +121,7 @@ struct Evaluation { } /// statements that are not executable - template constexpr static bool isOther(const A &) { + template constexpr static bool isOther() { if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || From 9af68698667eed2020278572fa301593a1a27d88 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Mon, 2 Dec 2019 16:25:53 -0800 Subject: [PATCH 044/123] fixes cmake bug for clang build --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 374a3e1afe33..49c1cda46acb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -335,7 +335,7 @@ if (LLVM_COMPILER_IS_GCC_COMPATIBLE) if (APPLE) # don't add --gcc-toolchain else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --gcc-toolchain=${CMAKE_CXX_COMPILER}") + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --gcc-toolchain=${CMAKE_CXX_COMPILER}") endif() endif() From 6eb318f918c60bdf763daa44c37f7e1729f705c5 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Tue, 3 Dec 2019 04:26:18 -0800 Subject: [PATCH 045/123] Address https://github.com/schweitzpgi/f18/pull/17 review comments - Rename `genMLIRLogicalConstant` to `genLogicalConstantAsI1` - Remove `getMLIRLogicalType` based on comment about `MLIR` meaning. - Add assert that Fortran function calls have a single result in MLIR - Editorial changes Also apply logical type control around intrinsic and user function calls. --- lib/burnside/convert-expr.cc | 51 +++++++++++++++++++++--------------- lib/burnside/convert-type.cc | 4 --- lib/burnside/convert-type.h | 2 -- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 1afe73f46a60..e6a57e853aef 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -118,11 +118,10 @@ class ExprLowering { } /// Generate a logical/boolean constant of `value` - M::Value *genMLIRLogicalConstant(M::MLIRContext *context, bool value) { - M::Type mlirLogicalType{getMLIRlogicalType(builder.getContext())}; - auto attr{builder.getIntegerAttr(mlirLogicalType, value ? 1 : 0)}; - return builder.create(getLoc(), mlirLogicalType, attr) - .getResult(); + M::Value *genLogicalConstantAsI1(M::MLIRContext *context, bool value) { + M::Type i1Type{M::IntegerType::get(1, builder.getContext())}; + auto attr{builder.getIntegerAttr(i1Type, value ? 1 : 0)}; + return builder.create(getLoc(), i1Type, attr).getResult(); } template @@ -382,9 +381,8 @@ class ExprLowering { // If an i1 result is needed, it does not make sens to convert between // `fir.logical` types to later convert back to the result to i1. return operand; - } else { - return builder.create(getLoc(), ty, operand); } + return builder.create(getLoc(), ty, operand); } template M::Value *genval(Ev::Parentheses const &) { TODO(); } @@ -394,7 +392,7 @@ class ExprLowering { auto restorer{common::ScopedSet(genLogicalAsI1, true)}; auto *context{builder.getContext()}; auto logical{genval(op.left())}; - auto one{genMLIRLogicalConstant(context, 1)}; + auto one{genLogicalConstantAsI1(context, true)}; return builder.create(getLoc(), logical, one).getResult(); } @@ -437,7 +435,7 @@ class ExprLowering { } else if constexpr (TC == LogicalCat) { auto opt{con.GetScalarValue()}; if (opt.has_value()) - return genMLIRLogicalConstant(builder.getContext(), opt->IsTrue()); + return genLogicalConstantAsI1(builder.getContext(), opt->IsTrue()); assert(false && "logical constant has no value"); return {}; } else if constexpr (TC == RealCat) { @@ -647,6 +645,12 @@ class ExprLowering { M::Type ty{getFIRType(builder.getContext(), defaults, TC, KIND)}; L::SmallVector operands; // Lower arguments + // For now, logical arguments for intrinsic are lowered to `fir.logical` + // so that TRANSFER can work. For some arguments, it could lead to useless + // conversions (e.g scalar MASK of MERGE will be converted to `i1`), but + // the generated code is at least correct. To improve this, the intrinsic + // lowering facility should control argument lowering. + auto restorer{common::ScopedSet(genLogicalAsI1, false)}; for (const auto &arg : funRef.arguments()) { if (auto *expr{Ev::UnwrapExpr>(arg)}) { operands.push_back(genval(*expr)); @@ -662,6 +666,9 @@ class ExprLowering { // TODO: explicit interface L::SmallVector argTypes; L::SmallVector operands; + // Logical arguments of user functions must be lowered to `fir.logical` + // and not `i1`. + auto restorer{common::ScopedSet(genLogicalAsI1, false)}; for (const auto &arg : funRef.arguments()) { assert( arg.has_value() && "optional argument requires explicit interface"); @@ -681,7 +688,12 @@ class ExprLowering { M::FunctionType funTy{ M::FunctionType::get(argTypes, resultType, builder.getContext())}; M::FuncOp func{getFunction(funRef.proc().GetName(), funTy)}; - return builder.create(getLoc(), func, operands).getResult(0); + auto call{builder.create(getLoc(), func, operands)}; + // For now, Fortran return value are implemented with a single MLIR + // function return value. + assert(call.getNumResults() == 1 && + "Expected exactly one result in FUNCTION call"); + return call.getResult(0); } } @@ -698,24 +710,21 @@ class ExprLowering { template M::Value *genval(Ev::Expr> const &exp) { - auto *rawResult{ - std::visit([&](const auto &e) { return genval(e); }, exp.u)}; + auto *result{std::visit([&](const auto &e) { return genval(e); }, exp.u)}; // Handle the `i1` to `fir.logical` conversions as needed. - auto *typedResult{rawResult}; - if (rawResult) { - M::Type type{rawResult->getType()}; + if (result) { + M::Type type{result->getType()}; if (type.isa()) { if (genLogicalAsI1) { - M::Type mlirLogicalType{getMLIRlogicalType(builder.getContext())}; - typedResult = builder.create( - getLoc(), mlirLogicalType, rawResult); + M::Type i1Type{M::IntegerType::get(1, builder.getContext())}; + result = builder.create(getLoc(), i1Type, result); } } else if (type.isa()) { if (!genLogicalAsI1) { M::Type firLogicalType{ getFIRType(builder.getContext(), defaults, LogicalCat, KIND)}; - typedResult = builder.create( - getLoc(), firLogicalType, rawResult); + result = + builder.create(getLoc(), firLogicalType, result); } } else if (auto seqType{type.dyn_cast_or_null()}) { // TODO: Conversions at array level should probably be avoided. @@ -725,7 +734,7 @@ class ExprLowering { assert(false && "unexpected logical type in expression"); } } - return typedResult; + return result; } template M::Value *gendef(const A &) { diff --git a/lib/burnside/convert-type.cc b/lib/burnside/convert-type.cc index cdc0eac83013..1b5cbe5575b3 100644 --- a/lib/burnside/convert-type.cc +++ b/lib/burnside/convert-type.cc @@ -388,7 +388,3 @@ M::Type Br::translateSymbolToFIRType(M::MLIRContext *context, M::Type Br::convertReal(M::MLIRContext *context, int kind) { return genFIRType(context, kind); } - -M::Type Br::getMLIRlogicalType(M::MLIRContext *context) { - return M::IntegerType::get(1, context); -} diff --git a/lib/burnside/convert-type.h b/lib/burnside/convert-type.h index ce7d3ab3a0e9..2c4f7b94741b 100644 --- a/lib/burnside/convert-type.h +++ b/lib/burnside/convert-type.h @@ -109,8 +109,6 @@ mlir::Type translateSymbolToFIRType(mlir::MLIRContext *ctxt, mlir::Type convertReal(mlir::MLIRContext *ctxt, int KIND); -mlir::Type getMLIRlogicalType(mlir::MLIRContext *ctxt); - } // burnside } // Fortran From 65d2d9b176c833ca773e40964cca8a63213842ea Mon Sep 17 00:00:00 2001 From: Steve Scalpone Date: Thu, 28 Nov 2019 23:57:05 -0800 Subject: [PATCH 046/123] [PATCH] F18 makefile for in-tree and out-of-tree builds. The PROJECT_SOURCE_DIR changes for in- and out-of-tree builds. Now, find the module files relative to the tools/f18 directory. Use the add_llvm_library feature LINK_LIBS to inherit the library attribute, e.g. shared. --- CMakeLists.txt | 232 ++++++++++++++---------------- lib/fir/CMakeLists.txt | 8 +- lib/fir/Transforms/CMakeLists.txt | 6 +- tools/f18/CMakeLists.txt | 8 +- tools/tco/CMakeLists.txt | 10 +- 5 files changed, 121 insertions(+), 143 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 49c1cda46acb..7e8d56a4f45b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,48 +13,45 @@ if (POLICY CMP0068) set(CMAKE_BUILD_WITH_INSTALL_NAME_DIR ON) endif() +if(POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) +endif() + if (POLICY CMP0077) - # FIXME??: Not yet certain this is the behavior we want... cmake_policy(SET CMP0077 NEW) endif() # If we are not building as part of LLVM (e.g., within the mono-repo), # build Flang as a standalone project. This assumes the use of an # existing LLVM install w/ external libraries. -if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - - project(Flang) - - # FIXME: We currently rely on llvm-config to find all the - # details for us. This is deprecated. The cmake files installed - # with LLVM should be able to detect the LLVM install automatically. - # If not, you can use the LLMV_DIR to specify the path containing - # LLVMConfig.cmake. - set(CONFIG_OUTPUT) - if (LLVM_CONFIG) - set(LLVM_CONFIG_FOUND 1) - message(STATUS "Found LLVM_CONFIG as ${LLVM_CONFIG}...") - message(DEPRECATION "Using llvm-config is deprecated. Use the \ - LLVM installed CMake files instead. \ - This should allow CMake to automatically find \ - find your install. You can also use LLVM_DIR \ - to specify the path to LLVMConfig.cmake.") - set(CONFIG_COMMAND ${LLVM_CONFIG} - "--assertion-mode" - "--bindir" - "--libdir" - "--includedir" - "--prefix" - "--src-root" - "--cmakedir") - execute_process( - COMMAND ${CONFIG_COMMAND} - RESULT_VARIABLE HAD_ERROR - OUTPUT_VARIABLE CONFIG_OUTPUT - ) - - if (NOT HAD_ERROR) - string(REGEX REPLACE +if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR ) + project(Flang) + + # Rely on llvm-config. + set(CONFIG_OUTPUT) + if(LLVM_CONFIG) + set (LLVM_CONFIG_FOUND 1) + message(STATUS "Found LLVM_CONFIG as ${LLVM_CONFIG}") + message(DEPRECATION "Using llvm-config is deprecated. Use the \ + LLVM installed CMake files instead. \ + This should allow CMake to automatically find \ + find your install. You can also use LLVM_DIR \ + to specify the path to LLVMConfig.cmake.") + set(CONFIG_COMMAND ${LLVM_CONFIG} + "--assertion-mode" + "--bindir" + "--libdir" + "--includedir" + "--prefix" + "--src-root" + "--cmakedir") + execute_process( + COMMAND ${CONFIG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE CONFIG_OUTPUT + ) + if(NOT HAD_ERROR) + string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" CONFIG_OUTPUT ${CONFIG_OUTPUT}) else() @@ -76,9 +73,10 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) file(TO_CMAKE_PATH ${LLVM_CONFIG_CMAKE_PATH} LLVM_CMAKE_PATH) endif() - if (NOT MSVC_IDE) + + if(NOT MSVC_IDE) set(LLVM_ENABLE_ASSERTIONS ${ENABLE_ASSERTIONS} - CACHE BOOL "Enable assertions") + CACHE BOOL "Enable assertions") # Assertions should follow llvm-config's. mark_as_advanced(LLVM_ENABLE_ASSERTIONS) endif() @@ -108,15 +106,35 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(LLVM_BINARY_DIR ${LLVM_OBJ_ROOT} CACHE PATH "Path to LLVM build tree") set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree") - # FIXME?? -- skipped tablegen stuff for now... + find_program(LLVM_TABLEGEN_EXE "llvm-tblgen" ${LLVM_TOOLS_BINARY_DIR} + NO_DEFAULT_PATH) + find_program(MLIR_TABLEGEN_EXE "mlir-tblgen" ${LLVM_TOOLS_BINARY_DIR} + NO_DEFAULT_PATH) + + # They are used as destination of target generators. + set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin) + set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX}) + if(WIN32 OR CYGWIN) + # DLL platform -- put DLLs into bin. + set(LLVM_SHLIB_OUTPUT_INTDIR ${LLVM_RUNTIME_OUTPUT_INTDIR}) + else() + set(LLVM_SHLIB_OUTPUT_INTDIR ${LLVM_LIBRARY_OUTPUT_INTDIR}) + endif() option(LLVM_ENABLE_WARNINGS "Enable compiler warnings." ON) option(LLVM_INSTALL_TOOLCHAIN_ONLY "Only include toolchain files in the 'install' target." OFF) + option(LLVM_FORCE_USE_OLD_HOST_TOOLCHAIN + "Set to ON to force using an old, unsupported host toolchain." OFF) + option(CLANG_ENABLE_BOOTSTRAP "Generate the clang bootstrap target" OFF) + option(LLVM_ENABLE_LIBXML2 "Use libxml2 if available." ON) + include(AddLLVM) + include(TableGen) include(HandleLLVMOptions) include(VersionFromVCS) + include(AddMLIR) set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}") @@ -126,48 +144,47 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) include_directories("${LLVM_BINARY_DIR}/include" "${LLVM_MAIN_INCLUDE_DIR}") link_directories("${LLVM_LIBRARY_DIR}") + set(MLIR_INCLUDE_DIRS "") - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) - - if (LLVM_INCLUDE_TESTS) - set(Python_ADDITIONAL_VERSIONS 2.7) + set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin ) + set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX} ) + set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX} ) + + if(LLVM_INCLUDE_TESTS) include(FindPythonInterp) - if (NOT PYTHONINTERP_FOUND) + if(NOT PYTHONINTERP_FOUND) message(FATAL_ERROR - "Unable to find Python interpreter, required for builds and testing. - Please install Python or specify the PYTHON_EXECUTABLE CMake variable.") +"Unable to find Python interpreter, required for builds and testing. + +Please install Python or specify the PYTHON_EXECUTABLE CMake variable.") endif() - if (${PYTHON_VERSION_STRING} VERSION_LESS 2.7) + if( ${PYTHON_VERSION_STRING} VERSION_LESS 2.7 ) message(FATAL_ERROR "Python 2.7 or newer is required") endif() - # Check prebuilt llvm/utils. - if (EXISTS ${LLVM_TOOLS_BINARY_DIR}/FileCheck${CMAKE_EXECUTABLE_SUFFIX} + if(EXISTS ${LLVM_TOOLS_BINARY_DIR}/FileCheck${CMAKE_EXECUTABLE_SUFFIX} AND EXISTS ${LLVM_TOOLS_BINARY_DIR}/count${CMAKE_EXECUTABLE_SUFFIX} AND EXISTS ${LLVM_TOOLS_BINARY_DIR}/not${CMAKE_EXECUTABLE_SUFFIX}) set(LLVM_UTILS_PROVIDED ON) endif() - if (EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) + if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) # Note: path not really used, except for checking if lit was found set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) - if (EXISTS ${LLVM_MAIN_SRC_DIR}/utils/llvm-lit) + if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/llvm-lit) add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/llvm-lit utils/llvm-lit) endif() - if (NOT LLVM_UTILS_PROVIDED) + if(NOT LLVM_UTILS_PROVIDED) add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/FileCheck utils/FileCheck) add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/count utils/count) add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/not utils/not) set(LLVM_UTILS_PROVIDED ON) set(FLANG_TEST_DEPS FileCheck count not) endif() - set(UNITTEST_DIR ${LLVM_MAIN_SRC_DIR}/utils/unittest) - if (EXISTS ${UNITTEST_DIR}/googletest/include/gtest/gtest.h + if(EXISTS ${UNITTEST_DIR}/googletest/include/gtest/gtest.h AND NOT EXISTS ${LLVM_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX} AND EXISTS ${UNITTEST_DIR}/CMakeLists.txt) add_subdirectory(${UNITTEST_DIR} utils/unittest) @@ -180,8 +197,9 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) DOC "Path to lit.py") endif() - if (LLVM_LIT) - # Define the default arguments to use with 'lit', and an option for the user to override. + if(LLVM_LIT) + # Define the default arguments to use with 'lit', and an option for the user + # to override. set(LIT_ARGS_DEFAULT "-sv") if (MSVC OR XCODE) set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar") @@ -189,7 +207,7 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit") # On Win32 hosts, provide an option to specify the path to the GnuWin32 tools. - if (WIN32 AND NOT CYGWIN) + if( WIN32 AND NOT CYGWIN ) set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools") endif() else() @@ -197,16 +215,31 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() endif() - set(FLANG_BUILD_STANDALONE 1) + set(FLANG_BUILT_STANDALONE 1) set(BACKEND_PACKAGE_STRING "LLVM ${LLVM_PACKAGE_VERSION}") else() set(BACKEND_PACKAGE_STRING "${PACKAGE_STRING}") + set(MLIR_BINARY_DIR ${LLVM_BINARY_DIR}/tools/mlir) + set(MLIR_SOURCE_DIR ${LLVM_SOURCE_DIR}/../mlir) + set(MLIR_INCLUDE_DIRS "${MLIR_BINARY_DIR}/include ${MLIR_SOURCE_DIR}/include") endif() # Make sure that our source directory is on the current cmake module path so that # we can include cmake files from this directory. list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") +if(LLVM_ENABLE_LIBXML2) + # Don't look for libxml if we're using MSan, since uninstrumented third party + # code may call MSan interceptors like strlen, leading to false positives. + if(NOT LLVM_USE_SANITIZER MATCHES "Memory.*") + set (LIBXML2_FOUND 0) + find_package(LibXml2 2.5.3 QUIET) + if (LIBXML2_FOUND) + set(FLANG_HAVE_LIBXML 1) + endif() + endif() +endif() + option(FLANG_LINK_WITH_FIR "Link Flang driver with FIR and LLVM" ON) if (LINK_WITH_FIR) message(STATUS "Linking driver with FIR and LLVM") @@ -216,14 +249,11 @@ endif() set(FLANG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(FLANG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) -set(MLIR_SOURCE_DIR ${LLVM_SOURCE_DIR}/projects/mlir) -set(MLIR_BINARY_DIR ${LLVM_BINARY_DIR}/projects/mlir) include_directories(BEFORE ${FLANG_BINARY_DIR}/include ${FLANG_SOURCE_DIR}/include - ${MLIR_BINARY_DIR}/include - ${MLIR_SOURCE_DIR}/include + ${MLIR_INCLUDE_DIRS} ) set(MLIR_MAIN_SRC_DIR ${MLIR_SOURCE_DIR}) @@ -249,9 +279,6 @@ if (NOT(FLANG_DEFAULT_RTLIB STREQUAL "")) "Default runtime library to use (\"libgcc\" or \"compiler-rt\", empty for platform default)" FORCE) endif() -# FIXME: Not handling OpenMP details here... - - set(FLANG_VENDOR ${PACKAGE_VENDOR} CACHE STRING "Vendor-specific text for showing with version information.") @@ -270,8 +297,6 @@ endif() set(FLANG_VENDOR_UTI "org.llvm.flang" CACHE STRING "Vendor-specific uti.") -# FIXME?? No Python bindings... - # The libdir suffix must exactly match whatever LLVM's configuration used. set(FLANG_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}") @@ -317,13 +342,22 @@ if (FLANG_LINK_WITH_FIR) message(STATUS "Flang using LLVM libraries: ${LLVM_COMMON_LIBS}") endif() -#set(FLANG_CXX_FLAGS "-std=c++17") - # Add appropriate flags for GCC if (LLVM_COMPILER_IS_GCC_COMPATIBLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -fno-rtti -fno-exceptions -Wall") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -fno-rtti -fno-exceptions -Wall -Wextra -Werror -Wcast-qual -Wimplicit-fallthrough -Wdelete-non-virtual-dtor") if (NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing -fno-semantic-interposition") + else () + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-command-line-argument -Wstring-conversion -Wcovered-switch-default") + # The following works around warnings in the f18 sources + check_cxx_compiler_flag("-Werror -Wdeprecated-copy" CXX_SUPPORTS_NO_DEPRECATED_COPY_FLAG) + if (CXX_SUPPORTS_NO_DEPRECATED_COPY_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-copy") + endif() + check_cxx_compiler_flag("-Werror -Wstring-conversion" CXX_SUPPORTS_NO_STRING_CONVERSION_FLAG) + if (CXX_SUPPORTS_NO_STRING_CONVERSION_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-string-conversion") + endif() endif () # Enable -pedantic for Flang even if it's not enabled for LLVM. @@ -331,14 +365,6 @@ if (LLVM_COMPILER_IS_GCC_COMPATIBLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic") endif () - if ((CMAKE_CXX_COMPILER_ID MATCHES "Clang")) - if (APPLE) - # don't add --gcc-toolchain - else() - #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --gcc-toolchain=${CMAKE_CXX_COMPILER}") - endif() - endif() - check_cxx_compiler_flag("-Werror -Wnested-anon-types" CXX_SUPPORTS_NO_NESTED_ANON_TYPES_FLAG) if (CXX_SUPPORTS_NO_NESTED_ANON_TYPES_FLAG) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nested-anon-types") @@ -348,6 +374,7 @@ if (LLVM_COMPILER_IS_GCC_COMPATIBLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --gcc-toolchain=${GCC}") endif () + set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -DCHECK=(void)") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUGF18") # Building shared libraries is death on performance with GCC by default @@ -383,13 +410,6 @@ endif() include(CMakeParseArguments) include(AddFlang) -# set(CMAKE_INCLUDE_CURRENT_DIR ON) - -#include_directories(BEFORE -# ${CMAKE_CURRENT_BINARY_DIR}/include -# ${CMAKE_CURRENT_SOURCE_DIR}/include -# ) - if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) install(DIRECTORY include/flang DESTINATION include @@ -410,10 +430,6 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) PATTERN "*.h" ) -# install(PROGRAMS utils/bash-autocomplete.sh -# DESTINATION share/flang -# ) - endif() add_definitions(-D_GNU_SOURCE) @@ -441,42 +457,6 @@ add_subdirectory(lib) add_subdirectory(tools) add_subdirectory(runtime) -#option(FLANG_BUILD_EXAMPLES "Build Flang example programs by default." OFF) -#add_subdirectory(examples) - -# FIXME?? Do we need an order file like Clang (for Darwin)? - -#if (FLANG_INCLUDE_TESTS) -# if (EXISTS ${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include/gtest/gtest.h) -# add_subdirectory(unittests) -# list(APPEND FLANG_TEST_DEPS FlangUnitTests) -# list(APPEND FLANG_TEST_PARAMS -# flang_unit_site_config=${CMAKE_CURRENT_BINARY_DIR}/test/Unit/lit.site.cfg -# ) -# endif() - -# add_subdirectory(test) -# -# if (FLANG_BUILT_STANDALONE) -# # Add a global check rule now that all subdirectories have been traversed -# # and we know the total set of lit testsuites. -# get_property(LLVM_LIT_TESTSUITES GLOBAL PROPERTY LLVM_LIT_TESTSUITES) -# get_property(LLVM_LIT_PARAMS GLOBAL PROPERTY LLVM_LIT_PARAMS) -# get_property(LLVM_LIT_DEPENDS GLOBAL PROPERTY LLVM_LIT_DEPENDS) -# get_property(LLVM_LIT_EXTRA_ARGS GLOBAL PROPERTY LLVM_LIT_EXTRA_ARGS) -# get_property(LLVM_ADDITIONAL_TEST_TARGETS -# GLOBAL PROPERTY LLVM_ADDITIONAL_TEST_TARGETS) -# -# add_lit_target(check-all -# "Running all regression tests" -# ${LLVM_LIT_TESTSUITES} -# PARAMS ${LLVM_LIT_PARAMS} -# DEPENDS ${LLVM_LIT_DEPENDS} ${LLVM_ADDITIONAL_TEST_TARGETS} -# ARGS ${LLVM_LIT_EXTRA_ARGS} -# ) -# endif() -#endif() - option(FLANG_INCLUDE_DOCS "Generate build targets for the Flang docs." ${LLVM_INCLUDE_DOCS}) diff --git a/lib/fir/CMakeLists.txt b/lib/fir/CMakeLists.txt index 54587daaf7f1..3df5d21397fc 100644 --- a/lib/fir/CMakeLists.txt +++ b/lib/fir/CMakeLists.txt @@ -24,11 +24,7 @@ add_llvm_library(FIR KindMapping.cpp StdConverter.cpp Tilikum.cpp -) - -add_dependencies(FIR FIROpsIncGen) - -target_link_libraries(FIR + LINK_LIBS MLIRTargetLLVMIR MLIRTargetLLVMIRModuleTranslation MLIREDSC @@ -42,6 +38,8 @@ target_link_libraries(FIR LLVMRemarks ) +add_dependencies(FIR FIROpsIncGen) + install (TARGETS FIR ARCHIVE DESTINATION lib LIBRARY DESTINATION lib diff --git a/lib/fir/Transforms/CMakeLists.txt b/lib/fir/Transforms/CMakeLists.txt index 6c0ad1179d2e..affe50fa2527 100644 --- a/lib/fir/Transforms/CMakeLists.txt +++ b/lib/fir/Transforms/CMakeLists.txt @@ -16,14 +16,12 @@ add_llvm_library(FIRTransforms CSE.cpp MemToReg.cpp RewriteLoop.cpp + LINK_LIBS + FIR ) add_dependencies(FIRTransforms FIROpsIncGen) -target_link_libraries(FIRTransforms - FIR -) - install (TARGETS FIRTransforms ARCHIVE DESTINATION lib LIBRARY DESTINATION lib diff --git a/tools/f18/CMakeLists.txt b/tools/f18/CMakeLists.txt index c151f05033a0..bbb93919825b 100644 --- a/tools/f18/CMakeLists.txt +++ b/tools/f18/CMakeLists.txt @@ -54,14 +54,14 @@ foreach(filename ${MODULES}) set(depends ${include}/__fortran_builtins.mod) endif() add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/${filename}.mod - COMMAND f18 -fparse-only -fdebug-semantics ${FLANG_SOURCE_DIR}/module/${filename}.f90 + COMMAND f18 -fparse-only -fdebug-semantics ${CMAKE_CURRENT_SOURCE_DIR}/../../module/${filename}.f90 WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include" - DEPENDS f18 ${FLANG_SOURCE_DIR}/module/${filename}.f90 + DEPENDS f18 ${CMAKE_CURRENT_SOURCE_DIR}/../../module/${filename}.f90 ) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/${filename}.f18.mod - COMMAND f18 -fparse-only -fdebug-semantics -module-suffix .f18.mod ${FLANG_SOURCE_DIR}/module/${filename}.f90 + COMMAND f18 -fparse-only -fdebug-semantics -module-suffix .f18.mod ${CMAKE_CURRENT_SOURCE_DIR}/../../module/${filename}.f90 WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include" - DEPENDS f18 ${FLANG_SOURCE_DIR}/module/${filename}.f90 + DEPENDS f18 ${CMAKE_CURRENT_SOURCE_DIR}/../../module/${filename}.f90 ) list(APPEND MODULE_FILES ${include}/${filename}.mod) list(APPEND MODULE_FILES ${include}/${filename}.f18.mod) diff --git a/tools/tco/CMakeLists.txt b/tools/tco/CMakeLists.txt index 4c927d8548c3..40a0d4112646 100644 --- a/tools/tco/CMakeLists.txt +++ b/tools/tco/CMakeLists.txt @@ -20,8 +20,11 @@ set(LIBS MLIRStandardOps ) -add_llvm_library(FirTcoLib tco.cpp) -target_link_libraries(FirTcoLib ${LIBS} FIRTransforms) +add_llvm_library(FirTcoLib + tco.cpp + LINK_LIBS + ${LIBS} +) set(EXE_LIBS ${LIBS} @@ -37,10 +40,9 @@ set(EXE_LIBS MLIRSupport MLIRTransforms MLIRVectorOps - MLIRVectorToLLVM ) add_llvm_tool(tco tco.cpp) llvm_update_compile_flags(tco) -whole_archive_link(tco ${LIBS}) +whole_archive_link(tco ${EXE_LIBS}) target_link_libraries(tco PRIVATE FIR MLIRIR FirTcoLib ${EXE_LIBS} LLVMSupport) From 2c5dd125bb0403e6dd0f9a1ac6124bac8e1cd0a3 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 3 Dec 2019 09:50:25 -0800 Subject: [PATCH 047/123] revert the cmake file changes for now --- lib/fir/CMakeLists.txt | 8 +++++--- lib/fir/Transforms/CMakeLists.txt | 6 ++++-- tools/f18/CMakeLists.txt | 8 ++++---- tools/tco/CMakeLists.txt | 10 ++++------ 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/fir/CMakeLists.txt b/lib/fir/CMakeLists.txt index 3df5d21397fc..54587daaf7f1 100644 --- a/lib/fir/CMakeLists.txt +++ b/lib/fir/CMakeLists.txt @@ -24,7 +24,11 @@ add_llvm_library(FIR KindMapping.cpp StdConverter.cpp Tilikum.cpp - LINK_LIBS +) + +add_dependencies(FIR FIROpsIncGen) + +target_link_libraries(FIR MLIRTargetLLVMIR MLIRTargetLLVMIRModuleTranslation MLIREDSC @@ -38,8 +42,6 @@ add_llvm_library(FIR LLVMRemarks ) -add_dependencies(FIR FIROpsIncGen) - install (TARGETS FIR ARCHIVE DESTINATION lib LIBRARY DESTINATION lib diff --git a/lib/fir/Transforms/CMakeLists.txt b/lib/fir/Transforms/CMakeLists.txt index affe50fa2527..6c0ad1179d2e 100644 --- a/lib/fir/Transforms/CMakeLists.txt +++ b/lib/fir/Transforms/CMakeLists.txt @@ -16,12 +16,14 @@ add_llvm_library(FIRTransforms CSE.cpp MemToReg.cpp RewriteLoop.cpp - LINK_LIBS - FIR ) add_dependencies(FIRTransforms FIROpsIncGen) +target_link_libraries(FIRTransforms + FIR +) + install (TARGETS FIRTransforms ARCHIVE DESTINATION lib LIBRARY DESTINATION lib diff --git a/tools/f18/CMakeLists.txt b/tools/f18/CMakeLists.txt index bbb93919825b..c151f05033a0 100644 --- a/tools/f18/CMakeLists.txt +++ b/tools/f18/CMakeLists.txt @@ -54,14 +54,14 @@ foreach(filename ${MODULES}) set(depends ${include}/__fortran_builtins.mod) endif() add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/${filename}.mod - COMMAND f18 -fparse-only -fdebug-semantics ${CMAKE_CURRENT_SOURCE_DIR}/../../module/${filename}.f90 + COMMAND f18 -fparse-only -fdebug-semantics ${FLANG_SOURCE_DIR}/module/${filename}.f90 WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include" - DEPENDS f18 ${CMAKE_CURRENT_SOURCE_DIR}/../../module/${filename}.f90 + DEPENDS f18 ${FLANG_SOURCE_DIR}/module/${filename}.f90 ) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/${filename}.f18.mod - COMMAND f18 -fparse-only -fdebug-semantics -module-suffix .f18.mod ${CMAKE_CURRENT_SOURCE_DIR}/../../module/${filename}.f90 + COMMAND f18 -fparse-only -fdebug-semantics -module-suffix .f18.mod ${FLANG_SOURCE_DIR}/module/${filename}.f90 WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include" - DEPENDS f18 ${CMAKE_CURRENT_SOURCE_DIR}/../../module/${filename}.f90 + DEPENDS f18 ${FLANG_SOURCE_DIR}/module/${filename}.f90 ) list(APPEND MODULE_FILES ${include}/${filename}.mod) list(APPEND MODULE_FILES ${include}/${filename}.f18.mod) diff --git a/tools/tco/CMakeLists.txt b/tools/tco/CMakeLists.txt index 40a0d4112646..4c927d8548c3 100644 --- a/tools/tco/CMakeLists.txt +++ b/tools/tco/CMakeLists.txt @@ -20,11 +20,8 @@ set(LIBS MLIRStandardOps ) -add_llvm_library(FirTcoLib - tco.cpp - LINK_LIBS - ${LIBS} -) +add_llvm_library(FirTcoLib tco.cpp) +target_link_libraries(FirTcoLib ${LIBS} FIRTransforms) set(EXE_LIBS ${LIBS} @@ -40,9 +37,10 @@ set(EXE_LIBS MLIRSupport MLIRTransforms MLIRVectorOps + MLIRVectorToLLVM ) add_llvm_tool(tco tco.cpp) llvm_update_compile_flags(tco) -whole_archive_link(tco ${EXE_LIBS}) +whole_archive_link(tco ${LIBS}) target_link_libraries(tco PRIVATE FIR MLIRIR FirTcoLib ${EXE_LIBS} LLVMSupport) From 48303439848303f3115e0e366ac067c6c8ae68cb Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 3 Dec 2019 12:50:01 -0800 Subject: [PATCH 048/123] revert more changes --- lib/burnside/ast-builder.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/burnside/ast-builder.h b/lib/burnside/ast-builder.h index 41d98d2d5902..189f5b212729 100644 --- a/lib/burnside/ast-builder.h +++ b/lib/burnside/ast-builder.h @@ -74,11 +74,11 @@ struct Evaluation { Evaluation(const A &a, const parser::CharBlock &pos, const std::optional &lab) : u{&a}, ux{StmtExtra{pos, lab}} { - static_assert(!isConstruct() && "must be a statement"); - if constexpr (isAction()) { + static_assert(!isConstruct(a) && "must be a statement"); + if constexpr (isAction(a)) { isActionStmt = true; } - if constexpr (isAction() || isOther()) { + if constexpr (isAction(a) || isOther(a)) { isStatement = true; } } @@ -86,12 +86,12 @@ struct Evaluation { /// Construct ctor template Evaluation(const A &a) : u{&a}, ux{std::list{}} { - static_assert(isConstruct() && "must be a construct"); + static_assert(isConstruct(a) && "must be a construct"); } /// statements that are executable (actions) - template constexpr static bool isAction() { - if constexpr (!isConstruct() && !isOther()) { + template constexpr static bool isAction(const A &a) { + if constexpr (!isConstruct(a) && !isOther(a)) { return true; } else { return false; @@ -99,7 +99,7 @@ struct Evaluation { } /// constructs (and directives) - template constexpr static bool isConstruct() { + template constexpr static bool isConstruct(const A &) { if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || @@ -121,7 +121,7 @@ struct Evaluation { } /// statements that are not executable - template constexpr static bool isOther() { + template constexpr static bool isOther(const A &) { if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || From f5ed379efc13da9376b755b25806feff1e877940 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 3 Dec 2019 14:15:15 -0800 Subject: [PATCH 049/123] rebase fix --- lib/fir/Tilikum.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index f97f1ca00bca..f8147d848bff 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -1355,19 +1355,14 @@ struct GlobalOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto global = M::cast(op); - auto tyAttr = M::TypeAttr::get(convertType(global.getType())); - M::UnitAttr isConst; - if (global.getAttr("constant")) - isConst = M::UnitAttr::get(global.getContext()); - auto name = M::StringAttr::get( + auto tyAttr = unwrap(convertType(global.getType())); + bool isConst = global.getAttr("constant") ? true : false; + auto name = global.getAttrOfType(M::SymbolTable::getSymbolAttrName()) - .getValue(), - global.getContext()); + .getValue(); M::Attribute value; - auto addrSpace = /* FIXME: hard-coded i32 here; is that ok? */ - rewriter.getI32IntegerAttr(0); - rewriter.replaceOpWithNewOp(global, tyAttr, isConst, - name, value, addrSpace); + rewriter.replaceOpWithNewOp( + global, tyAttr, isConst, M::LLVM::Linkage::External, name, value); return matchSuccess(); } }; From ecdd87cccde5d8e54d3c37281fab8a58aec9abc1 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 3 Dec 2019 14:28:07 -0800 Subject: [PATCH 050/123] changes --- tools/tco/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tco/CMakeLists.txt b/tools/tco/CMakeLists.txt index 4c927d8548c3..6037e10ea66a 100644 --- a/tools/tco/CMakeLists.txt +++ b/tools/tco/CMakeLists.txt @@ -37,7 +37,7 @@ set(EXE_LIBS MLIRSupport MLIRTransforms MLIRVectorOps - MLIRVectorToLLVM + MLIRVectorConversions ) add_llvm_tool(tco tco.cpp) From 098e4d55820f0c80249e602526f3a37da11734b9 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 3 Dec 2019 15:20:36 -0800 Subject: [PATCH 051/123] changes --- lib/burnside/ast-builder.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/burnside/ast-builder.h b/lib/burnside/ast-builder.h index 189f5b212729..41d98d2d5902 100644 --- a/lib/burnside/ast-builder.h +++ b/lib/burnside/ast-builder.h @@ -74,11 +74,11 @@ struct Evaluation { Evaluation(const A &a, const parser::CharBlock &pos, const std::optional &lab) : u{&a}, ux{StmtExtra{pos, lab}} { - static_assert(!isConstruct(a) && "must be a statement"); - if constexpr (isAction(a)) { + static_assert(!isConstruct() && "must be a statement"); + if constexpr (isAction()) { isActionStmt = true; } - if constexpr (isAction(a) || isOther(a)) { + if constexpr (isAction() || isOther()) { isStatement = true; } } @@ -86,12 +86,12 @@ struct Evaluation { /// Construct ctor template Evaluation(const A &a) : u{&a}, ux{std::list{}} { - static_assert(isConstruct(a) && "must be a construct"); + static_assert(isConstruct() && "must be a construct"); } /// statements that are executable (actions) - template constexpr static bool isAction(const A &a) { - if constexpr (!isConstruct(a) && !isOther(a)) { + template constexpr static bool isAction() { + if constexpr (!isConstruct() && !isOther()) { return true; } else { return false; @@ -99,7 +99,7 @@ struct Evaluation { } /// constructs (and directives) - template constexpr static bool isConstruct(const A &) { + template constexpr static bool isConstruct() { if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || @@ -121,7 +121,7 @@ struct Evaluation { } /// statements that are not executable - template constexpr static bool isOther(const A &) { + template constexpr static bool isOther() { if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || From c04a0351b8af8f7b80c2d142acea1e086cf2d484 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 3 Dec 2019 12:50:01 -0800 Subject: [PATCH 052/123] Remove the legacy "flatten" representation, which was an intermediate step to create CFGs. Replace the flat, linear representation with an AST to preserve the input structure. This is a "thin" AST as it is a tree structure superimposed upon the parse tree with pointers back into the parse tree representation. The AST is decorated with control annotations to provide both a way to construct a CFG and a way to preserve high-level operations on iteration spaces where possible. Burnside can use either the annotations (by group) or the specific pointers (case-by-case basis) to instantiate the FIR. --- include/fir/InternalNames.h | 5 + lib/burnside/CMakeLists.txt | 1 - lib/burnside/ast-builder.cc | 394 ++++++-- lib/burnside/ast-builder.h | 266 ++++-- lib/burnside/bridge.cc | 1781 ++++++++++++++++++----------------- lib/burnside/builder.h | 3 + lib/burnside/convert-type.h | 10 +- 7 files changed, 1429 insertions(+), 1031 deletions(-) diff --git a/include/fir/InternalNames.h b/include/fir/InternalNames.h index 83ad0b9e49ef..48ceb870ebca 100644 --- a/include/fir/InternalNames.h +++ b/include/fir/InternalNames.h @@ -75,6 +75,11 @@ struct NameMangler { llvm::Twine doVariable(llvm::ArrayRef modules, llvm::StringRef name); + /// Entry point for the PROGRAM (called by the runtime) + constexpr static llvm::StringRef getProgramEntry() { + return "__MAIN"; + } + private: llvm::Twine addAsString(std::int64_t i); llvm::Twine doKind(std::int64_t kind); diff --git a/lib/burnside/CMakeLists.txt b/lib/burnside/CMakeLists.txt index 1d9ef68e3af7..cdbdfbffa227 100644 --- a/lib/burnside/CMakeLists.txt +++ b/lib/burnside/CMakeLists.txt @@ -20,7 +20,6 @@ add_library(FortranBurnside builder.cc convert-expr.cc convert-type.cc - flattened.cc intrinsics.cc runtime.cc io.cc diff --git a/lib/burnside/ast-builder.cc b/lib/burnside/ast-builder.cc index f222622f4299..7562e4ff4d82 100644 --- a/lib/burnside/ast-builder.cc +++ b/lib/burnside/ast-builder.cc @@ -41,10 +41,13 @@ namespace { /// the bridge to one such instantiation. class ASTBuilder { public: - ASTBuilder() = default; + ASTBuilder() { + pgm = new AST::Program; + parents.push_back(pgm); + } /// Get the result - AST::Program result() { return pgm; } + AST::Program *result() { return pgm; } template constexpr bool Pre(const A &) { return true; } template constexpr void Post(const A &) {} @@ -72,15 +75,25 @@ class ASTBuilder { // Block data void Post(const Pa::BlockData &x) { - AST::BlockDataUnit unit{x}; + AST::BlockDataUnit unit{x, parents.back()}; addUnit(unit); } - // Evaluation + // + // Action statements + // void Post(const Pa::Statement &s) { addEval(makeEvalAction(s)); } + void Post(const Pa::UnlabeledStatement &s) { + addEval(makeEvalAction(s)); + } + + // + // Non-executable statements + // + void Post(const Pa::Statement> &s) { addEval(makeEvalIndirect(s)); } @@ -94,6 +107,111 @@ class ASTBuilder { addEval(makeEvalIndirect(s)); } + // + // Construct statements + // + + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + void Post(const Pa::Statement &s) { + addEval(makeEvalDirect(s)); + } + // Get rid of production wrapper + void Post(const Pa::UnlabeledStatement &s) { + addEval(std::visit( + [&](const auto &x) { + return AST::Evaluation{x, s.source, {}, parents.back()}; + }, + s.statement.u)); + } + void Post(const Pa::Statement &s) { + addEval(std::visit( + [&](const auto &x) { + return AST::Evaluation{x, s.source, s.label, parents.back()}; + }, + s.statement.u)); + } + + // + // Constructs (enter and exit) + // + bool Pre(const Pa::AssociateConstruct &c) { return enterConstruct(c); } bool Pre(const Pa::BlockConstruct &c) { return enterConstruct(c); } bool Pre(const Pa::CaseConstruct &c) { return enterConstruct(c); } @@ -132,13 +250,30 @@ class ASTBuilder { return std::visit( common::visitors{ [&](const Pa::ContinueStmt &x) { - return AST::Evaluation{x, s.source, s.label}; + return AST::Evaluation{x, s.source, s.label, parents.back()}; + }, + [&](const Pa::FailImageStmt &x) { + return AST::Evaluation{x, s.source, s.label, parents.back()}; + }, + [&](const auto &x) { + return AST::Evaluation{ + x.value(), s.source, s.label, parents.back()}; + }, + }, + s.statement.u); + } + AST::Evaluation makeEvalAction( + const Pa::UnlabeledStatement &s) { + return std::visit( + common::visitors{ + [&](const Pa::ContinueStmt &x) { + return AST::Evaluation{x, s.source, {}, parents.back()}; }, [&](const Pa::FailImageStmt &x) { - return AST::Evaluation{x, s.source, s.label}; + return AST::Evaluation{x, s.source, {}, parents.back()}; }, [&](const auto &x) { - return AST::Evaluation{x.value(), s.source, s.label}; + return AST::Evaluation{x.value(), s.source, {}, parents.back()}; }, }, s.statement.u); @@ -146,75 +281,98 @@ class ASTBuilder { template AST::Evaluation makeEvalIndirect(const Pa::Statement> &s) { - return AST::Evaluation{s.statement.value(), s.source, s.label}; + return AST::Evaluation{ + s.statement.value(), s.source, s.label, parents.back()}; + } + + template + AST::Evaluation makeEvalDirect(const Pa::Statement &s) { + return AST::Evaluation{s.statement, s.source, s.label, parents.back()}; } // When we enter a function-like structure, we want to build a new unit and // set the builder's cursors to point to it. template bool enterFunc(const A &f) { - AST::FunctionLikeUnit unit{f}; + auto &unit = addFunc(AST::FunctionLikeUnit{f, parents.back()}); funclist = &unit.funcs; pushEval(&unit.evals); - addFunc(unit); + parents.emplace_back(&unit); return true; } void exitFunc() { - funclist = nullptr; popEval(); + funclist = nullptr; + parents.pop_back(); } // When we enter a construct structure, we want to build a new construct and // set the builder's evaluation cursor to point to it. template bool enterConstruct(const A &c) { - AST::Evaluation con{c}; - addEval(con); - pushEval(con.getConstructEvals()); + auto &con = addEval(AST::Evaluation{c, parents.back()}); + con.subs = new std::list(); + pushEval(con.subs); + parents.emplace_back(&con); return true; } - void exitConstruct() { popEval(); } + void exitConstruct() { + popEval(); + parents.pop_back(); + } // When we enter a module structure, we want to build a new module and // set the builder's function cursor to point to it. template bool enterModule(const A &f) { - AST::ModuleLikeUnit unit{f}; + auto &unit = addUnit(AST::ModuleLikeUnit{f, parents.back()}); funclist = &unit.funcs; - addUnit(unit); + parents.emplace_back(&unit); return true; } - void exitModule() { funclist = nullptr; } + void exitModule() { + funclist = nullptr; + parents.pop_back(); + } - template void addUnit(const A &unit) { - pgm.units.emplace_back(unit); + template A &addUnit(const A &unit) { + pgm->getUnits().emplace_back(unit); + return std::get(pgm->getUnits().back()); } - template void addFunc(const A &func) { - if (funclist) + template A &addFunc(const A &func) { + if (funclist) { funclist->emplace_back(func); - else - addUnit(func); + return funclist->back(); + } + return addUnit(func); } - void addEval(const AST::Evaluation &eval) { - assert(funclist); - evallist.back()->emplace_back(eval); + /// move the Evaluation to the end of the current list + AST::Evaluation &addEval(AST::Evaluation &&eval) { + assert(funclist && "not in a function"); + assert(evallist.size() > 0); + evallist.back()->emplace_back(std::move(eval)); + return evallist.back()->back(); } + /// push a new list on the stack of Evaluation lists void pushEval(std::list *eval) { - assert(funclist); - evallist.push_back(eval); + assert(funclist && "not in a function"); + assert(eval && eval->empty() && "evaluation list isn't correct"); + evallist.emplace_back(eval); } + /// pop the current list and return to the last Evaluation list void popEval() { - assert(funclist); + assert(funclist && "not in a function"); evallist.pop_back(); } - AST::Program pgm; + AST::Program *pgm; std::list *funclist{nullptr}; std::vector *> evallist; + std::vector parents; }; template constexpr bool hasErrLabel(const A &stmt) { @@ -315,57 +473,126 @@ void ioLabel(AST::Evaluation &e, const A *s, AST::Evaluation *cstr) { void annotateEvalListCFG( std::list &evals, AST::Evaluation *cstr) { bool nextIsTarget = false; - for (auto e : evals) { + for (auto &e : evals) { e.isTarget = nextIsTarget; nextIsTarget = false; if (e.isConstruct()) { annotateEvalListCFG(*e.getConstructEvals(), &e); // assume that the entry and exit are both possible branch targets - e.isTarget = nextIsTarget = true; - continue; + nextIsTarget = true; + } + if (e.isActionStmt() && e.lab.has_value()) { + e.isTarget = true; } std::visit( common::visitors{ - [&](Pa::BackspaceStmt *s) { ioLabel(e, s, cstr); }, - [&](Pa::CallStmt *s) { altRet(e, s, cstr); }, - [&](Pa::CloseStmt *s) { ioLabel(e, s, cstr); }, - [&](Pa::CycleStmt *) { e.setCFG(AST::CFGAnnotation::Goto, cstr); }, - [&](Pa::EndfileStmt *s) { ioLabel(e, s, cstr); }, - [&](Pa::ExitStmt *) { e.setCFG(AST::CFGAnnotation::Goto, cstr); }, - [&](Pa::FailImageStmt *) { - e.setCFG(AST::CFGAnnotation::Return, cstr); + [&](const Pa::BackspaceStmt *s) { ioLabel(e, s, cstr); }, + [&](const Pa::CallStmt *s) { altRet(e, s, cstr); }, + [&](const Pa::CloseStmt *s) { ioLabel(e, s, cstr); }, + [&](const Pa::CycleStmt *) { + e.setCFG(AST::CFGAnnotation::Goto, cstr); + }, + [&](const Pa::EndfileStmt *s) { ioLabel(e, s, cstr); }, + [&](const Pa::ExitStmt *) { + e.setCFG(AST::CFGAnnotation::Goto, cstr); + }, + [&](const Pa::FailImageStmt *) { + e.setCFG(AST::CFGAnnotation::Terminate, cstr); + }, + [&](const Pa::FlushStmt *s) { ioLabel(e, s, cstr); }, + [&](const Pa::GotoStmt *) { + e.setCFG(AST::CFGAnnotation::Goto, cstr); + }, + [&](const Pa::IfStmt *) { + e.setCFG(AST::CFGAnnotation::CondGoto, cstr); }, - [&](Pa::FlushStmt *s) { ioLabel(e, s, cstr); }, - [&](Pa::GotoStmt *) { e.setCFG(AST::CFGAnnotation::Goto, cstr); }, - [&](Pa::IfStmt *) { e.setCFG(AST::CFGAnnotation::CondGoto, cstr); }, - [&](Pa::InquireStmt *s) { ioLabel(e, s, cstr); }, - [&](Pa::OpenStmt *s) { ioLabel(e, s, cstr); }, - [&](Pa::ReadStmt *s) { ioLabel(e, s, cstr); }, - [&](Pa::ReturnStmt *) { + [&](const Pa::InquireStmt *s) { ioLabel(e, s, cstr); }, + [&](const Pa::OpenStmt *s) { ioLabel(e, s, cstr); }, + [&](const Pa::ReadStmt *s) { ioLabel(e, s, cstr); }, + [&](const Pa::ReturnStmt *) { e.setCFG(AST::CFGAnnotation::Return, cstr); }, - [&](Pa::RewindStmt *s) { ioLabel(e, s, cstr); }, - [&](Pa::StopStmt *) { e.setCFG(AST::CFGAnnotation::Return, cstr); }, - [&](Pa::WaitStmt *s) { ioLabel(e, s, cstr); }, - [&](Pa::WriteStmt *s) { ioLabel(e, s, cstr); }, - [&](Pa::ArithmeticIfStmt *) { + [&](const Pa::RewindStmt *s) { ioLabel(e, s, cstr); }, + [&](const Pa::StopStmt *) { + e.setCFG(AST::CFGAnnotation::Terminate, cstr); + }, + [&](const Pa::WaitStmt *s) { ioLabel(e, s, cstr); }, + [&](const Pa::WriteStmt *s) { ioLabel(e, s, cstr); }, + [&](const Pa::ArithmeticIfStmt *) { e.setCFG(AST::CFGAnnotation::Switch, cstr); }, - [&](Pa::AssignedGotoStmt *) { + [&](const Pa::AssignedGotoStmt *) { e.setCFG(AST::CFGAnnotation::IndGoto, cstr); }, - [&](Pa::ComputedGotoStmt *) { + [&](const Pa::ComputedGotoStmt *) { + e.setCFG(AST::CFGAnnotation::Switch, cstr); + }, + [&](const Pa::WhereStmt *) { + // fir.loop + fir.where around the next stmt + e.isTarget = true; + e.setCFG(AST::CFGAnnotation::Iterative, cstr); + }, + [&](const Pa::ForallStmt *) { + // fir.loop around the next stmt + e.isTarget = true; + e.setCFG(AST::CFGAnnotation::Iterative, cstr); + }, + [&](AST::CGJump &) { e.setCFG(AST::CFGAnnotation::Goto, cstr); }, + [&](const Pa::EndAssociateStmt *) { e.isTarget = true; }, + [&](const Pa::EndBlockStmt *) { e.isTarget = true; }, + [&](const Pa::SelectCaseStmt *) { + e.setCFG(AST::CFGAnnotation::Switch, cstr); + }, + [&](const Pa::CaseStmt *) { e.isTarget = true; }, + [&](const Pa::EndSelectStmt *) { e.isTarget = true; }, + [&](const Pa::EndChangeTeamStmt *) { e.isTarget = true; }, + [&](const Pa::EndCriticalStmt *) { e.isTarget = true; }, + [&](const Pa::NonLabelDoStmt *) { + e.isTarget = true; + e.setCFG(AST::CFGAnnotation::Iterative, cstr); + }, + [&](const Pa::EndDoStmt *) { + e.isTarget = true; + e.setCFG(AST::CFGAnnotation::Goto, cstr); + }, + [&](const Pa::IfThenStmt *) { + e.setCFG(AST::CFGAnnotation::CondGoto, cstr); + }, + [&](const Pa::ElseIfStmt *) { + e.setCFG(AST::CFGAnnotation::CondGoto, cstr); + }, + [&](const Pa::ElseStmt *) { e.isTarget = true; }, + [&](const Pa::EndIfStmt *) { e.isTarget = true; }, + [&](const Pa::SelectRankStmt *) { e.setCFG(AST::CFGAnnotation::Switch, cstr); }, - [](auto *) { /* do nothing */ }, + [&](const Pa::SelectRankCaseStmt *) { e.isTarget = true; }, + [&](const Pa::SelectTypeStmt *) { + e.setCFG(AST::CFGAnnotation::Switch, cstr); + }, + [&](const Pa::TypeGuardStmt *) { e.isTarget = true; }, + [&](const Pa::WhereConstruct *) { + // mark the WHERE as if it were a DO loop + e.isTarget = true; + e.setCFG(AST::CFGAnnotation::Iterative, cstr); + }, + [&](const Pa::WhereConstructStmt *) { + e.setCFG(AST::CFGAnnotation::CondGoto, cstr); + }, + [&](const Pa::MaskedElsewhereStmt *) { + e.isTarget = true; + e.setCFG(AST::CFGAnnotation::CondGoto, cstr); + }, + [&](const Pa::ElsewhereStmt *) { e.isTarget = true; }, + [&](const Pa::EndWhereStmt *) { e.isTarget = true; }, + [&](const Pa::ForallConstructStmt *) { + e.isTarget = true; + e.setCFG(AST::CFGAnnotation::Iterative, cstr); + }, + [&](const Pa::EndForallStmt *) { e.isTarget = true; }, + [](const auto *) { /* do nothing */ }, }, e.u); - if (e.isActionStmt && - std::get>( - std::get(e.ux)) - .has_value()) { - e.isTarget = true; - } } } @@ -375,8 +602,9 @@ inline void annotateFuncCFG(AST::FunctionLikeUnit &flu) { } // namespace -Br::AST::FunctionLikeUnit::FunctionLikeUnit(const Pa::MainProgram &f) - : ProgramUnit{&f} { +Br::AST::FunctionLikeUnit::FunctionLikeUnit( + const Pa::MainProgram &f, const AST::ParentType &parent) + : ProgramUnit{&f, parent} { auto &ps{std::get>>(f.t)}; if (ps.has_value()) { const Pa::Statement &s{ps.value()}; @@ -385,57 +613,63 @@ Br::AST::FunctionLikeUnit::FunctionLikeUnit(const Pa::MainProgram &f) funStmts.push_back(&std::get>(f.t)); } -Br::AST::FunctionLikeUnit::FunctionLikeUnit(const Pa::FunctionSubprogram &f) - : ProgramUnit{&f} { +Br::AST::FunctionLikeUnit::FunctionLikeUnit( + const Pa::FunctionSubprogram &f, const AST::ParentType &parent) + : ProgramUnit{&f, parent} { funStmts.push_back(&std::get>(f.t)); funStmts.push_back(&std::get>(f.t)); } -Br::AST::FunctionLikeUnit::FunctionLikeUnit(const Pa::SubroutineSubprogram &f) - : ProgramUnit{&f} { +Br::AST::FunctionLikeUnit::FunctionLikeUnit( + const Pa::SubroutineSubprogram &f, const AST::ParentType &parent) + : ProgramUnit{&f, parent} { funStmts.push_back(&std::get>(f.t)); funStmts.push_back(&std::get>(f.t)); } Br::AST::FunctionLikeUnit::FunctionLikeUnit( - const Pa::SeparateModuleSubprogram &f) - : ProgramUnit{&f} { + const Pa::SeparateModuleSubprogram &f, const AST::ParentType &parent) + : ProgramUnit{&f, parent} { funStmts.push_back(&std::get>(f.t)); funStmts.push_back(&std::get>(f.t)); } -Br::AST::ModuleLikeUnit::ModuleLikeUnit(const Pa::Module &m) : ProgramUnit{&m} { +Br::AST::ModuleLikeUnit::ModuleLikeUnit( + const Pa::Module &m, const AST::ParentType &parent) + : ProgramUnit{&m, parent} { modStmts.push_back(&std::get>(m.t)); modStmts.push_back(&std::get>(m.t)); } -Br::AST::ModuleLikeUnit::ModuleLikeUnit(const Pa::Submodule &m) - : ProgramUnit{&m} { +Br::AST::ModuleLikeUnit::ModuleLikeUnit( + const Pa::Submodule &m, const AST::ParentType &parent) + : ProgramUnit{&m, parent} { modStmts.push_back(&std::get>(m.t)); modStmts.push_back(&std::get>(m.t)); } -Br::AST::BlockDataUnit::BlockDataUnit(const Pa::BlockData &db) - : ProgramUnit{&db} {} +Br::AST::BlockDataUnit::BlockDataUnit( + const Pa::BlockData &db, const AST::ParentType &parent) + : ProgramUnit{&db, parent} {} -AST::Program Br::createAST(const Pa::Program &root) { +AST::Program *Br::createAST(const Pa::Program &root) { ASTBuilder walker; Walk(root, walker); return walker.result(); } void Br::annotateControl(AST::Program &ast) { - for (auto unit : ast.units) { + for (auto &unit : ast.getUnits()) { std::visit(common::visitors{ [](AST::BlockDataUnit &) {}, [](AST::FunctionLikeUnit &f) { annotateFuncCFG(f); - for (auto s : f.funcs) { + for (auto &s : f.funcs) { annotateFuncCFG(s); } }, [](AST::ModuleLikeUnit &u) { - for (auto f : u.funcs) { + for (auto &f : u.funcs) { annotateFuncCFG(f); } }, diff --git a/lib/burnside/ast-builder.h b/lib/burnside/ast-builder.h index 41d98d2d5902..0b64868598e5 100644 --- a/lib/burnside/ast-builder.h +++ b/lib/burnside/ast-builder.h @@ -21,6 +21,14 @@ namespace Fortran::burnside { namespace AST { +struct Evaluation; +struct Program; +struct ModuleLikeUnit; +struct FunctionLikeUnit; + +using ParentType = + std::variant; + enum class CFGAnnotation { None, Goto, @@ -28,111 +36,146 @@ enum class CFGAnnotation { IndGoto, IoSwitch, Switch, - Return + Iterative, + Return, + Terminate +}; + +/// Compiler-generated jump +/// +/// This is used to convert implicit control-flow edges to explicit form in the +/// decorated AST +struct CGJump { + CGJump(Evaluation *to) : target{to} {} + Evaluation *target{nullptr}; }; +/// is `A` a construct (or directive)? +template constexpr static bool isConstruct() { + return std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v; +} + /// Function-like units can contains lists of evaluations. These can be /// (simple) statements or constructs, where a construct contains its own /// evaluations. struct Evaluation { - using EvalVariant = std::variant; - using StmtExtra = std::tuple>; + const parser::OpenMPConstruct *, const parser::OmpEndLoopDirective *, + // construct statements + const parser::AssociateStmt *, const parser::EndAssociateStmt *, + const parser::BlockStmt *, const parser::EndBlockStmt *, + const parser::SelectCaseStmt *, const parser::CaseStmt *, + const parser::EndSelectStmt *, const parser::ChangeTeamStmt *, + const parser::EndChangeTeamStmt *, const parser::CriticalStmt *, + const parser::EndCriticalStmt *, const parser::NonLabelDoStmt *, + const parser::EndDoStmt *, const parser::IfThenStmt *, + const parser::ElseIfStmt *, const parser::ElseStmt *, + const parser::EndIfStmt *, const parser::SelectRankStmt *, + const parser::SelectRankCaseStmt *, const parser::SelectTypeStmt *, + const parser::TypeGuardStmt *, const parser::WhereConstructStmt *, + const parser::MaskedElsewhereStmt *, const parser::ElsewhereStmt *, + const parser::EndWhereStmt *, const parser::ForallConstructStmt *, + const parser::EndForallStmt *>; Evaluation() = delete; + Evaluation(const Evaluation &) = default; - /// Statement ctor + /// General ctor template Evaluation(const A &a, const parser::CharBlock &pos, - const std::optional &lab) - : u{&a}, ux{StmtExtra{pos, lab}} { - static_assert(!isConstruct() && "must be a statement"); - if constexpr (isAction()) { - isActionStmt = true; - } - if constexpr (isAction() || isOther()) { - isStatement = true; - } - } + const std::optional &lab, const ParentType &p) + : u{&a}, parent{p}, pos{pos}, lab{lab} {} + + /// Compiler-generated jump + Evaluation(const CGJump &jump, const ParentType &p) + : u{jump}, parent{p}, cfg{CFGAnnotation::Goto} {} /// Construct ctor template - Evaluation(const A &a) : u{&a}, ux{std::list{}} { - static_assert(isConstruct() && "must be a construct"); + Evaluation(const A &a, const ParentType &parent) : u{&a}, parent{parent} { + static_assert(AST::isConstruct(), "must be a construct"); } - /// statements that are executable (actions) - template constexpr static bool isAction() { - if constexpr (!isConstruct() && !isOther()) { - return true; - } else { - return false; - } + /// is `A` executable (an action statement or compiler generated)? + template constexpr static bool isAction(const A &a) { + return !AST::isConstruct() && !isOther(a); } - /// constructs (and directives) - template constexpr static bool isConstruct() { - if constexpr (std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v) { - return true; - } else { - return false; - } + /// is `A` a compiler-generated evaluation? + template constexpr static bool isGenerated(const A &) { + return std::is_same_v; } - /// statements that are not executable - template constexpr static bool isOther() { - if constexpr (std::is_same_v || + /// is `A` not an executable statement? + template constexpr static bool isOther(const A &) { + return std::is_same_v || std::is_same_v || std::is_same_v || - std::is_same_v) { - return true; - } else { - return false; - } + std::is_same_v; + } + + constexpr bool isActionStmt() const { + return std::visit(common::visitors{ + [](auto *p) { return isAction(*p); }, + [](auto &r) { return isGenerated(r); }, + }, + u); } - constexpr bool isStmt() const { return isStatement; } + constexpr bool isStmt() const { + return std::visit(common::visitors{ + [](auto *p) { return isAction(*p) || isOther(*p); }, + [](auto &r) { return isGenerated(r); }, + }, + u); + } constexpr bool isConstruct() const { return !isStmt(); } /// Set the type of originating control flow type for this evaluation. @@ -147,12 +190,12 @@ struct Evaluation { /// Is this evaluation a control-flow target? (The AST must be annotated) bool isControlTarget() const { return isTarget; } - /// Set the hasBranches flag iff this evaluation (a construct) contains + /// Set the containsBranches flag iff this evaluation (a construct) contains /// control flow - void setBranches() { hasBranches = true; } + void setBranches() { containsBranches = true; } constexpr std::list *getConstructEvals() { - return isStatement ? nullptr : std::get_if>(&ux); + return isStmt() ? nullptr : subs; } /// Set that the construct `cstr` (if not a nullptr) has branches. @@ -163,24 +206,27 @@ struct Evaluation { } EvalVariant u; - std::variant> ux; + ParentType parent; + parser::CharBlock pos; + std::optional lab; + std::list *subs{nullptr}; // construct sub-statements CFGAnnotation cfg{CFGAnnotation::None}; - bool isStatement{false}; - bool isActionStmt{false}; - bool isTarget{false}; - bool hasBranches{false}; + bool isTarget{false}; // this evaluation is a control target + bool containsBranches{false}; // construct contains branches }; /// A program is a list of program units. /// These units can be function like, module like, or block data struct ProgramUnit { - template ProgramUnit(A *ptr) : p{ptr} {} + template + ProgramUnit(A *ptr, const ParentType &parent) : p{ptr}, parent{parent} {} std::variant p; + ParentType parent; }; /// Function-like units have similar structure. They all can contain executable @@ -197,15 +243,40 @@ struct FunctionLikeUnit : public ProgramUnit { const parser::Statement *, const parser::Statement *>; - FunctionLikeUnit(const parser::MainProgram &f); - FunctionLikeUnit(const parser::FunctionSubprogram &f); - FunctionLikeUnit(const parser::SubroutineSubprogram &f); - FunctionLikeUnit(const parser::SeparateModuleSubprogram &f); + FunctionLikeUnit(const parser::MainProgram &f, const ParentType &parent); + FunctionLikeUnit( + const parser::FunctionSubprogram &f, const ParentType &parent); + FunctionLikeUnit( + const parser::SubroutineSubprogram &f, const ParentType &parent); + FunctionLikeUnit( + const parser::SeparateModuleSubprogram &f, const ParentType &parent); + + bool isMainProgram() { + return std::get_if *>( + &funStmts.back()); + } + const parser::FunctionStmt *isFunction() { + return isA(); + } + const parser::SubroutineStmt *isSubroutine() { + return isA(); + } + const parser::MpSubprogramStmt *isMPSubp() { + return isA(); + } - const semantics::Scope *scope{nullptr}; - std::list funStmts; - std::list evals; - std::list funcs; + const semantics::Scope *scope{nullptr}; // scope from front-end + std::list funStmts; // begin/end pair + std::list evals; // statements + std::list funcs; // internal procedures + +private: + template const A *isA() { + if (auto p = std::get_if *>(&funStmts.front())) { + return &(*p)->statement; + } + return nullptr; + } }; /// Module-like units have similar structure. They all can contain a list of @@ -218,8 +289,8 @@ struct ModuleLikeUnit : public ProgramUnit { const parser::Statement *, const parser::Statement *>; - ModuleLikeUnit(const parser::Module &m); - ModuleLikeUnit(const parser::Submodule &m); + ModuleLikeUnit(const parser::Module &m, const ParentType &parent); + ModuleLikeUnit(const parser::Submodule &m, const ParentType &parent); ~ModuleLikeUnit() = default; const semantics::Scope *scope{nullptr}; @@ -228,21 +299,28 @@ struct ModuleLikeUnit : public ProgramUnit { }; struct BlockDataUnit : public ProgramUnit { - BlockDataUnit(const parser::BlockData &db); + BlockDataUnit(const parser::BlockData &db, const ParentType &parent); }; /// A Program is the top-level AST struct Program { using Units = std::variant; + + std::list &getUnits() { return units; } + +private: std::list units; }; } // namespace AST /// Create an AST from the parse tree -AST::Program createAST(const parser::Program &root); +AST::Program *createAST(const parser::Program &root); /// Decorate the AST with control flow annotations +/// +/// The AST must be decorated with control-flow annotations to prepare it for +/// use in generating a CFG-like structure. void annotateControl(AST::Program &ast); } // namespace burnside diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index f571f9985f0f..babe24387614 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -13,14 +13,14 @@ // limitations under the License. #include "bridge.h" +#include "ast-builder.h" #include "builder.h" #include "convert-expr.h" #include "convert-type.h" -#include "flattened.h" #include "intrinsics.h" #include "io.h" #include "runtime.h" -#include "../parser/parse-tree-visitor.h" +#include "../parser/parse-tree.h" #include "../semantics/tools.h" #include "fir/FIRDialect.h" #include "fir/FIROps.h" @@ -35,10 +35,14 @@ #undef TODO #define TODO() assert(false && "not yet implemented") +#undef SOFT_TODO +#define SOFT_TODO() \ + llvm::errs() << __FILE__ << ":" << __LINE__ << " not yet implemented\n"; + namespace Br = Fortran::burnside; namespace Co = Fortran::common; namespace Ev = Fortran::evaluate; -namespace Fl = Fortran::burnside::flat; +namespace L = llvm; namespace M = mlir; namespace Pa = Fortran::parser; namespace Se = Fortran::semantics; @@ -48,88 +52,299 @@ using namespace Fortran::burnside; namespace { -using SomeExpr = Ev::Expr; +using CFGSinkListType = L::SmallVector; +using CFGMapType = L::DenseMap; -constexpr bool isStopStmt(Pa::StopStmt::Kind kind) { - return kind == Pa::StopStmt::Kind::Stop; +constexpr static bool isStopStmt(const Pa::StopStmt &stm) { + return std::get(stm.t) == Pa::StopStmt::Kind::Stop; } -constexpr bool firLoopOp{false}; - -/// Converter from Fortran to FIR -class FIRConverter { - using LabelMapType = std::map; - using Closure = std::function; - - struct DoBoundsInfo { - M::Value *doVar; - - M::Value *counter; - M::Value *stepExpr; - M::Operation *condition; - }; +/// Traverse the AST and complete the CFG by drawing the arcs, pruning unused +/// potential targets, making implied jumps explicit, etc. +class CfgBuilder { - M::MLIRContext &mlirContext; - const Pa::CookedSource *cooked; - M::ModuleOp &module; - Co::IntrinsicTypeDefaultKinds const &defaults; - std::unique_ptr builder; - LabelMapType blockMap; // map from flattened labels to MLIR blocks - std::list edgeQ; - std::map doMap; - SymMap symbolMap; - IntrinsicLibrary intrinsics; - Pa::CharBlock lastKnownPos; - bool noInsPt{false}; + AST::Evaluation *getEvalByLabel(const Pa::Label &label) { + auto iter = labels.find(label); + if (iter != labels.end()) { + return iter->second; + } + return nullptr; + } - inline M::OpBuilder &build() { return *builder.get(); } - inline M::ModuleOp &getMod() { return module; } - inline LabelMapType &blkMap() { return blockMap; } - void setCurrentPos(const Pa::CharBlock &pos) { lastKnownPos = pos; } + /// Collect all the potential targets and initialize them to unreferenced + void resetPotentialTargets(std::list &evals) { + for (auto &e : evals) { + if (e.isTarget) { + e.isTarget = false; + } + if (e.lab.has_value()) { + labels.try_emplace(*e.lab, &e); + } + if (e.subs) { + resetPotentialTargets(*e.subs); + } + } + } - /// Convert a parser CharBlock to a Location - M::Location toLocation(const Pa::CharBlock &cb) { - return parserPosToLoc(mlirContext, cooked, cb); + /// cache ASSIGN statements that may yield a live branch target + void cacheAssigns(std::list &evals) { + for (auto &e : evals) { + std::visit(Co::visitors{ + [&](const Pa::AssignStmt *stmt) { + auto *trg = getEvalByLabel(std::get(stmt->t)); + auto *sym = std::get(stmt->t).symbol; + assert(sym); + auto jter = assignedGotoMap.find(sym); + if (jter == assignedGotoMap.end()) { + std::list lst = {trg}; + assignedGotoMap.try_emplace(sym, lst); + } else { + jter->second.emplace_back(trg); + } + }, + [](auto) { /* do nothing */ }, + }, + e.u); + if (e.subs) { + cacheAssigns(*e.subs); + } + } } - M::Location toLocation() { return toLocation(lastKnownPos); } - /// Construct the type of an Expr expression - M::Type exprType(const SomeExpr *expr) { - return translateSomeExprToFIRType(&mlirContext, defaults, expr); + void wrapIterationSpaces(std::list &evals) { + for (auto &e : evals) { + if (e.subs) { + wrapIterationSpaces(*e.subs); + } + } } - M::Type refExprType(const SomeExpr *expr) { - auto type{translateSomeExprToFIRType(&mlirContext, defaults, expr)}; - return fir::ReferenceType::get(type); + + /// Add source->sink edge to CFG map + void addSourceToSink(AST::Evaluation *src, AST::Evaluation *snk) { + auto iter = cfgMap.find(src); + if (iter == cfgMap.end()) { + CFGSinkListType sink{snk}; + cfgEdgeSetPool.emplace_back(std::move(sink)); + auto rc{cfgMap.try_emplace(src, &cfgEdgeSetPool.back())}; + assert(rc.second && "insert failed unexpectedly"); + (void)rc; // for release build + return; + } + for (auto *s : *iter->second) + if (s == snk) { + return; + } + iter->second->push_back(snk); + } + + void addSourceToSink(AST::Evaluation *src, const Pa::Label &label) { + auto iter = labels.find(label); + assert(iter != labels.end()); + addSourceToSink(src, iter->second); + } + + /// Find the next ELSE IF, ELSE or END IF statement in the list + template A nextFalseTarget(A iter, const A &endi) { + for (; iter != endi; ++iter) + if (std::visit(Co::visitors{ + [&](const Pa::ElseIfStmt *) { return true; }, + [&](const Pa::ElseStmt *) { return true; }, + [&](const Pa::EndIfStmt *) { return true; }, + [](auto) { return false; }, + }, + iter->u)) { + break; + } + return iter; } - M::Type getDefaultIntegerType() { - return getFIRType(&mlirContext, defaults, IntegerCat); + /// Add branches for this IF block like construct. + /// Branch to the "true block", the "false block", and from the end of the + /// true block to the end of the construct. + template + void doNextIfBlock(std::list &evals, AST::Evaluation &e, + const A &iter, const A &endif) { + A i{iter}; + A j{nextFalseTarget(++i, endif)}; + auto *cstr = std::get(e.parent); + AST::CGJump jump{&*endif}; + A k{evals.insert(j, AST::Evaluation{std::move(jump), j->parent})}; + if (i == j) { + // block was empty, so adjust "true" target + i = k; + } + addSourceToSink(&*k, cstr); + addSourceToSink(&e, &*i); + addSourceToSink(&e, &*j); + } + + /// Determine which branch targets are reachable. The target map must + /// already be initialized. + void reachabilityAnalysis(std::list &evals) { + for (auto iter = evals.begin(); iter != evals.end(); ++iter) { + auto &e = *iter; + switch (e.cfg) { + case AST::CFGAnnotation::None: + // do nothing - does not impart control flow + break; + case AST::CFGAnnotation::Goto: + std::visit( + Co::visitors{ + [&](const Pa::CycleStmt *) { + // FIXME: deal with construct name + auto *cstr = std::get(e.parent); + addSourceToSink(&e, &cstr->subs->front()); + }, + [&](const Pa::ExitStmt *) { + // FIXME: deal with construct name + auto *cstr = std::get(e.parent); + addSourceToSink(&e, &cstr->subs->back()); + }, + [&](const Pa::GotoStmt *stmt) { addSourceToSink(&e, stmt->v); }, + [&](const Pa::EndDoStmt *) { + // the END DO is the loop exit landing pad + // insert a JUMP as the backedge right before the END DO + auto *cstr = std::get(e.parent); + AST::CGJump jump{&cstr->subs->front()}; + AST::Evaluation jumpEval{std::move(jump), iter->parent}; + evals.insert(iter, std::move(jumpEval)); + addSourceToSink(&e, &cstr->subs->front()); + }, + [](auto) { assert(false); }, + }, + e.u); + break; + case AST::CFGAnnotation::CondGoto: + std::visit(Co::visitors{ + [&](const Pa::IfStmt *) { + // check if these are marked; they must targets here + auto i{iter}; + addSourceToSink(&e, &*(++i)); + addSourceToSink(&e, &*(++i)); + }, + [&](const Pa::IfThenStmt *) { + doNextIfBlock(evals, e, iter, evals.end()); + }, + [&](const Pa::ElseIfStmt *) { + doNextIfBlock(evals, e, iter, evals.end()); + }, + [](const Pa::WhereConstructStmt *stmt) { TODO(); }, + [](const Pa::MaskedElsewhereStmt *stmt) { TODO(); }, + [](auto) { assert(false); }, + }, + e.u); + break; + case AST::CFGAnnotation::IndGoto: + std::visit( + Co::visitors{ + [&](const Pa::AssignedGotoStmt *stmt) { + auto *sym = std::get(stmt->t).symbol; + if (assignedGotoMap.find(sym) != assignedGotoMap.end()) + for (auto *x : assignedGotoMap[sym]) { + addSourceToSink(&e, x); + } + for (auto &l : std::get>(stmt->t)) { + addSourceToSink(&e, l); + } + }, + [](auto) { assert(false); }, + }, + e.u); + break; + case AST::CFGAnnotation::IoSwitch: + std::visit(Co::visitors{ + [](const Pa::BackspaceStmt *stmt) { TODO(); }, + [](const Pa::CloseStmt *stmt) { TODO(); }, + [](const Pa::EndfileStmt *stmt) { TODO(); }, + [](const Pa::FlushStmt *stmt) { TODO(); }, + [](const Pa::InquireStmt *stmt) { TODO(); }, + [](const Pa::OpenStmt *stmt) { TODO(); }, + [](const Pa::ReadStmt *stmt) { TODO(); }, + [](const Pa::RewindStmt *stmt) { TODO(); }, + [](const Pa::WaitStmt *stmt) { TODO(); }, + [](const Pa::WriteStmt *stmt) { TODO(); }, + [](auto) { assert(false); }, + }, + e.u); + break; + case AST::CFGAnnotation::Switch: + std::visit(Co::visitors{ + [](const Pa::CallStmt *stmt) { TODO(); }, + [](const Pa::ArithmeticIfStmt *stmt) { TODO(); }, + [](const Pa::ComputedGotoStmt *stmt) { TODO(); }, + [](const Pa::SelectCaseStmt *stmt) { TODO(); }, + [](const Pa::SelectRankStmt *stmt) { TODO(); }, + [](const Pa::SelectTypeStmt *stmt) { TODO(); }, + [](auto) { assert(false); }, + }, + e.u); + break; + case AST::CFGAnnotation::Iterative: + std::visit(Co::visitors{ + [](const Pa::NonLabelDoStmt *stmt) { TODO(); }, + [](const Pa::WhereStmt *stmt) { TODO(); }, + [](const Pa::ForallStmt *stmt) { TODO(); }, + [](const Pa::WhereConstruct *stmt) { TODO(); }, + [](const Pa::ForallConstructStmt *stmt) { TODO(); }, + [](auto) { assert(false); }, + }, + e.u); + break; + case AST::CFGAnnotation::Return: + // do nothing - exits the function + break; + case AST::CFGAnnotation::Terminate: + // do nothing - exits the function + break; + } + if (e.subs) { + reachabilityAnalysis(*e.subs); + } + } } - M::Type getDefaultLogicalType() { - return getFIRType(&mlirContext, defaults, LogicalCat); + + CFGMapType &cfgMap; + std::list &cfgEdgeSetPool; + + L::DenseMap labels; + std::map> assignedGotoMap; + +public: + CfgBuilder(CFGMapType &m, std::list &p) + : cfgMap{m}, cfgEdgeSetPool{p} {} + + void run(AST::FunctionLikeUnit &func) { + resetPotentialTargets(func.evals); + cacheAssigns(func.evals); + wrapIterationSpaces(func.evals); + reachabilityAnalysis(func.evals); } +}; + +/// Converter from AST to FIR +/// +/// After building the AST and decorating it, the FirConverter processes that +/// representation and lowers it to the FIR executable representation. +class FirConverter { + using LabelMapType = std::map; + using Closure = std::function; - M::Value *createFIRAddr(M::Location loc, const SomeExpr *expr) { + // + // Helper function members + // + + M::Value *createFIRAddr(M::Location loc, const Se::SomeExpr *expr) { return createSomeAddress( - loc, build(), *expr, symbolMap, defaults, intrinsics); + loc, *builder, *expr, symbolMap, defaults, intrinsics); } - M::Value *createFIRExpr(M::Location loc, const SomeExpr *expr) { + + M::Value *createFIRExpr(M::Location loc, const Se::SomeExpr *expr) { return createSomeExpression( - loc, build(), *expr, symbolMap, defaults, intrinsics); + loc, *builder, *expr, symbolMap, defaults, intrinsics); } - M::Value *createLogicalExprAsI1(M::Location loc, const SomeExpr *expr) { + M::Value *createLogicalExprAsI1(M::Location loc, const Se::SomeExpr *expr) { return createI1LogicalExpression( - loc, build(), *expr, symbolMap, defaults, intrinsics); - } - M::Value *createTemp(M::Type type, Se::Symbol *symbol = nullptr) { - return createTemporary(toLocation(), build(), symbolMap, type, symbol); - } - - M::FuncOp genFunctionFIR(llvm::StringRef callee, M::FunctionType funcTy) { - if (auto func{getNamedFunction(getMod(), callee)}) { - return func; - } - return createFunction(getMod(), callee, funcTy); + loc, *builder, *expr, symbolMap, defaults, intrinsics); } M::FuncOp genRuntimeFunction(RuntimeEntryCode rec, int kind) { @@ -137,908 +352,772 @@ class FIRConverter { getRuntimeEntryName(rec), getRuntimeEntryType(rec, mlirContext, kind)); } - template DoBoundsInfo *getBoundsInfo(const T &linearOp) { - auto &st{std::get>(linearOp.v->t)}; - setCurrentPos(st.source); - auto *s{&st.statement}; - auto iter{doMap.find(s)}; - if (iter != doMap.end()) { - return &iter->second; + M::FuncOp genFunctionFIR(L::StringRef callee, M::FunctionType funcTy) { + if (auto func{getNamedFunction(module, callee)}) { + return func; } - assert(false && "DO context not present"); - return nullptr; + return createFunction(module, callee, funcTy); } - // Simple scalar expression builders - // TODO: handle REAL and COMPLEX (iff needed) - template - M::Value *genCompare(M::Value *lhs, M::Value *rhs) { - auto lty{lhs->getType()}; - assert(lty == rhs->getType()); - if (lty.isIntOrIndex()) { - return build().create(lhs->getLoc(), ICMPOPC, lhs, rhs); - } - if (fir::LogicalType::kindof(lty.getKind())) { - return build().create(lhs->getLoc(), ICMPOPC, lhs, rhs); - } - if (fir::CharacterType::kindof(lty.getKind())) { - // return build().create(lhs->getLoc(), ); - return {}; - } - assert(false && "cannot generate operation on this type"); - return {}; - } - M::Value *genGE(M::Value *lhs, M::Value *rhs) { - return genCompare(lhs, rhs); + bool inMainProgram(AST::Evaluation *cstr) { + return std::visit( + Co::visitors{ + [](AST::FunctionLikeUnit *c) { return c->isMainProgram(); }, + [&](AST::Evaluation *c) { return inMainProgram(c); }, + [](auto *) { return false; }, + }, + cstr->parent); } - M::Value *genLE(M::Value *lhs, M::Value *rhs) { - return genCompare(lhs, rhs); + const Pa::SubroutineStmt *inSubroutine(AST::Evaluation *cstr) { + return std::visit( + Co::visitors{ + [](AST::FunctionLikeUnit *c) { return c->isSubroutine(); }, + [&](AST::Evaluation *c) { return inSubroutine(c); }, + [](auto *) -> const Pa::SubroutineStmt * { return nullptr; }, + }, + cstr->parent); } - M::Value *genEQ(M::Value *lhs, M::Value *rhs) { - return genCompare(lhs, rhs); + const Pa::FunctionStmt *inFunction(AST::Evaluation *cstr) { + return std::visit( + Co::visitors{ + [](AST::FunctionLikeUnit *c) { return c->isFunction(); }, + [&](AST::Evaluation *c) { return inFunction(c); }, + [](auto *) -> const Pa::FunctionStmt * { return nullptr; }, + }, + cstr->parent); } - M::Value *genAND(M::Value *lhs, M::Value *rhs) { - return build().create(lhs->getLoc(), lhs, rhs); + const Pa::MpSubprogramStmt *inMPSubp(AST::Evaluation *cstr) { + return std::visit( + Co::visitors{ + [](AST::FunctionLikeUnit *c) { return c->isMPSubp(); }, + [&](AST::Evaluation *c) { return inMPSubp(c); }, + [](auto *) -> const Pa::MpSubprogramStmt * { return nullptr; }, + }, + cstr->parent); } - template void genFIR(const Co::Indirection &indirection) { - genFIR(indirection.value()); + template + const Se::SomeExpr *getScalarExprOfTuple(const A &tuple) { + return Se::GetExpr(std::get(tuple)); + } + template const Se::SomeExpr *getExprOfTuple(const A &tuple) { + return Se::GetExpr(std::get(tuple)); + } + /// Get the condition expression for a CondGoto evaluation + const Se::SomeExpr *getEvaluationCondition(AST::Evaluation &eval) { + return std::visit(Co::visitors{ + [&](const Pa::IfStmt *stmt) { + return getScalarExprOfTuple(stmt->t); + }, + [&](const Pa::IfThenStmt *stmt) { + return getScalarExprOfTuple(stmt->t); + }, + [&](const Pa::ElseIfStmt *stmt) { + return getScalarExprOfTuple(stmt->t); + }, + [&](const Pa::WhereConstructStmt *stmt) { + return getExprOfTuple(stmt->t); + }, + [&](const Pa::MaskedElsewhereStmt *stmt) { + return getExprOfTuple(stmt->t); + }, + [](auto) -> const Se::SomeExpr * { + assert( + false && "unexpected conditional branch case"); + return nullptr; + }, + }, + eval.u); + } + + // + // Function-like AST entry and exit statements + // + + void genFIR(const Pa::Statement &stmt, std::string &name, + Se::Symbol const *&) { + setCurrentPosition(stmt.source); + name = mangler.getProgramEntry(); + } + void genFIR(const Pa::Statement &stmt, std::string &, + Se::Symbol const *&) { + setCurrentPosition(stmt.source); + genFIR(stmt.statement); + } + void genFIR(const Pa::Statement &stmt, std::string &name, + Se::Symbol const *&symbol) { + setCurrentPosition(stmt.source); + auto &n{std::get(stmt.statement.t)}; + name = n.ToString(); + symbol = n.symbol; } - template void genFIR(const Pa::Statement &stmt) { - setCurrentPos(stmt.source); + void genFIR(const Pa::Statement &stmt, std::string &, + Se::Symbol const *&) { + setCurrentPosition(stmt.source); genFIR(stmt.statement); } - template void genFIR(const std::list &list) { - for (auto &a : list) { - genFIR(a); - } + void genFIR(const Pa::Statement &stmt, std::string &name, + Se::Symbol const *&symbol) { + setCurrentPosition(stmt.source); + auto &n{std::get(stmt.statement.t)}; + name = n.ToString(); + symbol = n.symbol; + } + void genFIR(const Pa::Statement &stmt, std::string &, + Se::Symbol const *&) { + setCurrentPosition(stmt.source); + genFIR(stmt.statement); + } + void genFIR(const Pa::Statement &stmt, + std::string &name, Se::Symbol const *&symbol) { + setCurrentPosition(stmt.source); + auto &n{stmt.statement.v}; + name = n.ToString(); + symbol = n.symbol; } - template void genFIROnVariant(const A &variant) { - std::visit([&](auto &s) { genFIR(s); }, variant.u); + void genFIR(const Pa::Statement &stmt, std::string &, + Se::Symbol const *&) { + setCurrentPosition(stmt.source); + genFIR(stmt.statement); } - void genFIR(AnalysisData &ad, std::list &operations); + // + // Termination of symbolically referenced execution units + // - // Control flow destination - void genFIR(bool lastWasLabel, const Fl::LabelOp &op) { - if (lastWasLabel) { - blkMap().insert({op.get(), build().getInsertionBlock()}); - } else { - auto *currBlock{build().getInsertionBlock()}; - auto *newBlock{createBlock(&build())}; - blkMap().insert({op.get(), newBlock}); - if (!noInsPt) { - build().setInsertionPointToEnd(currBlock); - build().create(toLocation(), newBlock); - } - build().setInsertionPointToStart(newBlock); - } + /// END of program + /// + /// Generate the cleanup block before the program exits + void genFIRProgramExit() { builder->create(toLocation()); } + void genFIR(const Pa::EndProgramStmt &) { genFIRProgramExit(); } + + /// END of procedure-like constructs + /// + /// Generate the cleanup block before the procedure exits + void genFIRFunctionReturn(const Pa::FunctionStmt *stmt) { + auto &name = std::get(stmt->t); + assert(name.symbol); + const auto &details{name.symbol->get()}; + M::Value *resultRef{symbolMap.lookupSymbol(details.result())}; + // FIXME: what happens if result was never referenced before and hence no + // temp was created? + assert(resultRef); + M::Value *r{builder->create(toLocation(), resultRef)}; + builder->create(toLocation(), r); + } + void genFIR(const Pa::EndFunctionStmt &stmt) { + genFIRFunctionReturn(inFunction(currentEvaluation)); + } + template void genFIRProcedureExit(const A *) { + // FIXME: alt-returns + builder->create(toLocation()); + } + void genFIR(const Pa::EndSubroutineStmt &stmt) { + genFIRProcedureExit(inSubroutine(currentEvaluation)); + } + void genFIR(const Pa::EndMpSubprogramStmt &stmt) { + genFIRProcedureExit(inMPSubp(currentEvaluation)); + } + + // + // Statements that have control-flow semantics + // + + // Conditional goto control-flow semantics + void genFIREvalCondGoto(AST::Evaluation &eval) { + genFIR(eval); + auto targets{findTargetsOf(eval)}; + auto *expr{getEvaluationCondition(eval)}; + assert(expr && "condition expression missing"); + auto *cond{createLogicalExprAsI1(toLocation(), expr)}; + genFIRCondBranch(cond, targets[0], targets[1]); + } + + void genFIRCondBranch( + M::Value *cond, AST::Evaluation *trueDest, AST::Evaluation *falseDest) { + using namespace std::placeholders; + localEdgeQ.emplace_back(std::bind( + [](M::OpBuilder *builder, M::Block *block, M::Value *cnd, + AST::Evaluation *trueDest, AST::Evaluation *falseDest, + M::Location location, const LabelMapType &map) { + L::SmallVector blk; + builder->setInsertionPointToEnd(block); + auto tdp{map.find(trueDest)}; + auto fdp{map.find(falseDest)}; + assert(tdp != map.end() && fdp != map.end()); + builder->create( + location, cnd, tdp->second, blk, fdp->second, blk); + }, + builder, builder->getInsertionBlock(), cond, trueDest, falseDest, + toLocation(), _1)); + } + + // Goto control-flow semantics + // + // These are unconditional jumps. There is nothing to evaluate. + void genFIREvalGoto(AST::Evaluation &eval) { + using namespace std::placeholders; + localEdgeQ.emplace_back(std::bind( + [](M::OpBuilder *builder, M::Block *block, AST::Evaluation *dest, + M::Location location, const LabelMapType &map) { + builder->setInsertionPointToEnd(block); + assert(map.find(dest) != map.end() && "no destination"); + builder->create(location, map.find(dest)->second); + }, + builder, builder->getInsertionBlock(), findSinkOf(eval), toLocation(), + _1)); } - // Goto statements - void genFIR(const Fl::GotoOp &op) { - auto iter{blkMap().find(op.target)}; - if (iter != blkMap().end()) { - build().create(toLocation(), iter->second); - } else { - using namespace std::placeholders; - edgeQ.emplace_back(std::bind( - [](M::OpBuilder *builder, M::Block *block, Fl::LabelMention dest, - M::Location location, const LabelMapType &map) { - builder->setInsertionPointToEnd(block); - assert(map.find(dest) != map.end() && "no destination"); - builder->create(location, map.find(dest)->second); - }, - &build(), build().getInsertionBlock(), op.target, toLocation(), _1)); - } - noInsPt = true; + // Indirect goto control-flow semantics + // + // For assigned gotos, which is an obsolescent feature. Lower to a switch. + void genFIREvalIndGoto(AST::Evaluation &eval) { + genFIR(eval); + // FIXME } - void genFIR(AnalysisData &ad, const Fl::ReturnOp &op) { - std::visit(Co::visitors{ - [&](const Pa::ReturnStmt *stmt) { genReturnStmt(ad, stmt); }, - [&](const auto *stmt) { genFIR(*stmt); }, - }, - op.u); - noInsPt = true; + + // IO statements that have control-flow semantics + // + // First lower the IO statement and then do the multiway switch op + void genFIREvalIoSwitch(AST::Evaluation &eval) { + genFIR(eval); + genFIRIOSwitch(eval); } - void genFIR(const Fl::ConditionalGotoOp &op) { - std::visit( - [&](const auto *stmt) { genFIR(*stmt, op.trueLabel, op.falseLabel); }, - op.u); - noInsPt = true; + void genFIRIOSwitch(AST::Evaluation &) { TODO(); } + + // Iterative loop control-flow semantics + void genFIREvalIterative(AST::Evaluation &eval) { + // FIXME } - // IO statement with END, ERR, EOR labels - void genFIR(const Fl::SwitchIOOp &op) { - auto loc{toLocation(op.source)}; - (void)loc; - TODO(); + // Return from subprogram control-flow semantics + void genFIREvalReturn(AST::Evaluation &eval) { + // Handled case-by-case + // FIXME: think about moving the case code here } - // CALL with alt-return value returned - void genFIR(const Fl::SwitchOp &op, const Pa::CallStmt &stmt) { - auto loc{toLocation(op.source)}; + // Multiway switch control-flow semantics + void genFIREvalSwitch(AST::Evaluation &eval) { + genFIR(eval); + // FIXME + } + + // Terminate process control-flow semantics + // + // Call a runtime routine that does not return + void genFIREvalTerminate(AST::Evaluation &eval) { + genFIR(eval); + builder->create(toLocation()); + } + + // No control-flow + void genFIREvalNone(AST::Evaluation &eval) { genFIR(eval); } + + void genFIR(const Pa::CallStmt &stmt) { + // FIXME handle alternate return + auto loc{toLocation()}; (void)loc; TODO(); } - void genFIR(const Fl::SwitchOp &op, const Pa::ComputedGotoStmt &stmt) { - auto loc{toLocation(op.source)}; + void genFIR(const Pa::IfStmt &) { TODO(); } + void genFIR(const Pa::WaitStmt &) { TODO(); } + void genFIR(const Pa::WhereStmt &) { TODO(); } + void genFIR(const Pa::ComputedGotoStmt &stmt) { auto *exp{Se::GetExpr(std::get(stmt.t))}; - auto *e1{createFIRExpr(loc, exp)}; + auto *e1{createFIRExpr(toLocation(), exp)}; (void)e1; TODO(); } - void genFIR(const Fl::SwitchOp &op, const Pa::ArithmeticIfStmt &stmt) { - auto loc{toLocation(op.source)}; + void genFIR(const Pa::ForallStmt &) { TODO(); } + void genFIR(const Pa::ArithmeticIfStmt &stmt) { auto *exp{Se::GetExpr(std::get(stmt.t))}; - auto *e1{createFIRExpr(loc, exp)}; + auto *e1{createFIRExpr(toLocation(), exp)}; (void)e1; TODO(); } - M::Value *fromCaseValue(const M::Location &locs, const Pa::CaseValue &val) { - return createFIRExpr(locs, Se::GetExpr(val)); - } - void genFIR(const Fl::SwitchOp &op, const Pa::CaseConstruct &stmt); - void genFIR(const Fl::SwitchOp &op, const Pa::SelectRankConstruct &stmt); - void genFIR(const Fl::SwitchOp &op, const Pa::SelectTypeConstruct &stmt); - void genFIR(const Fl::SwitchOp &op) { - std::visit([&](auto *construct) { genFIR(op, *construct); }, op.u); - noInsPt = true; - } - - void genFIR(AnalysisData &ad, const Fl::ActionOp &op); - - void pushDoContext(const Pa::NonLabelDoStmt *doStmt, - M::Value *doVar = nullptr, M::Value *counter = nullptr, - M::Value *stepExpr = nullptr) { - doMap.emplace(doStmt, DoBoundsInfo{doVar, counter, stepExpr, 0}); - } - - void genLoopEnterFIR(const Pa::LoopControl::Bounds &bounds, - const Pa::NonLabelDoStmt *stmt, const Pa::CharBlock &source) { - auto loc{toLocation(source)}; - // evaluate e1, e2 [, e3] ... - auto *e1{createFIRExpr(loc, Se::GetExpr(bounds.lower))}; - auto *e2{createFIRExpr(loc, Se::GetExpr(bounds.upper))}; - if (firLoopOp) { - std::vector step; - if (bounds.step.has_value()) - step.push_back(createFIRExpr(loc, Se::GetExpr(bounds.step))); - auto loopOp{build().create(loc, e1, e2, step)}; - auto *block = createBlock(&build(), &loopOp.getOperation()->getRegion(0)); - block->addArgument(M::IndexType::get(build().getContext())); - return; - } - auto *nameExpr{bounds.name.thing.symbol}; - auto *name{createTemp(getDefaultIntegerType(), nameExpr)}; - M::Value *e3; - if (bounds.step.has_value()) { - auto *stepExpr{Se::GetExpr(bounds.step)}; - e3 = createFIRExpr(loc, stepExpr); - } else { - auto attr{build().getIntegerAttr(e2->getType(), 1)}; - e3 = build().create(loc, attr); - } - // name <- e1 - build().create(loc, e1, name); - auto tripCounter{createTemp(getDefaultIntegerType())}; - // See 11.1.7.4.1, para. 1, item (3) - // totalTrips ::= iteration count = a - // where a = (e2 - e1 + e3) / e3 if a > 0 and 0 otherwise - auto c1{build().create(loc, e2, e1)}; - auto c2{build().create(loc, c1.getResult(), e3)}; - auto c3{build().create(loc, c2.getResult(), e3)}; - auto *totalTrips{c3.getResult()}; - build().create(loc, totalTrips, tripCounter); - pushDoContext(stmt, name, tripCounter, e3); - } - - void genLoopEnterFIR(const Pa::ScalarLogicalExpr &logicalExpr, - const Pa::NonLabelDoStmt *stmt, const Pa::CharBlock &source) { - // See 11.1.7.4.1, para. 2 - // See BuildLoopHeaderExpression() - pushDoContext(stmt); - } - - void genLoopEnterFIR(const Pa::LoopControl::Concurrent &concurrent, - const Pa::NonLabelDoStmt *stmt, const Pa::CharBlock &source) { - // See 11.1.7.4.2 - TODO(); - } + void genFIR(const Pa::AssignedGotoStmt &) { TODO(); } - void genEnterFIR(const Pa::DoConstruct &construct) { - auto &stmt{std::get>(construct.t)}; - setCurrentPos(stmt.source); + void genFIR(const Pa::AssociateConstruct &) { TODO(); } + void genFIR(const Pa::BlockConstruct &) { TODO(); } + void genFIR(const Pa::CaseConstruct &) { TODO(); } + void genFIR(const Pa::ChangeTeamConstruct &) { TODO(); } + void genFIR(const Pa::CriticalConstruct &) { TODO(); } + void genFIR(const Pa::DoConstruct &d) { + auto &stmt{std::get>(d.t)}; const Pa::NonLabelDoStmt &ss{stmt.statement}; auto &ctrl{std::get>(ss.t)}; if (ctrl.has_value()) { - std::visit([&](const auto &x) { genLoopEnterFIR(x, &ss, stmt.source); }, - ctrl->u); + // std::visit([&](const auto &x) { genLoopEnterFIR(x, &ss, stmt.source); + // }, ctrl->u); } else { // loop forever (See 11.1.7.4.1, para. 2) - pushDoContext(&ss); + // pushDoContext(&ss); } + TODO(); } + void genFIR(const Pa::IfConstruct &cst) { TODO(); } + void genFIR(const Pa::SelectRankConstruct &) { TODO(); } + void genFIR(const Pa::SelectTypeConstruct &) { TODO(); } + void genFIR(const Pa::WhereConstruct &) { TODO(); } /// Lower FORALL construct (See 10.2.4) - void genEnterFIR(const Pa::ForallConstruct &forall) { + void genFIR(const Pa::ForallConstruct &forall) { auto &stmt{std::get>(forall.t)}; - setCurrentPos(stmt.source); + setCurrentPosition(stmt.source); auto &fas{stmt.statement}; auto &ctrl{std::get>(fas.t).value()}; - auto &bld{build()}; (void)ctrl; - (void)bld; // FIXME - // bld.create(); for (auto &s : std::get>(forall.t)) { - genFIROnVariant(s); + std::visit(Co::visitors{ + [&](const Pa::Statement &b) { + setCurrentPosition(b.source); + genFIR(b.statement); + }, + [&](const Pa::Statement &b) { + setCurrentPosition(b.source); + genFIR(b.statement); + }, + [&](const Pa::WhereConstruct &b) { genFIR(b); }, + [&](const Co::Indirection &b) { + genFIR(b.value()); + }, + [&](const Pa::Statement &b) { + setCurrentPosition(b.source); + genFIR(b.statement); + }, + }, + s.u); } TODO(); } - void genFIR(const Pa::ForallConstruct &forall) { genEnterFIR(forall); } - void genFIR(const Pa::ForallAssignmentStmt &s) { genFIROnVariant(s); } - - void genEnterFIR(const Pa::WhereConstruct &where) { TODO(); } - void genFIR(const Pa::WhereConstruct &where) { genEnterFIR(where); } - - void genFIR(const Fl::BeginOp &op) { - std::visit([&](auto *construct) { genEnterFIR(*construct); }, op.u); + void genFIR(const Pa::ForallAssignmentStmt &s) { + std::visit([&](auto &b) { genFIR(b); }, s.u); + } + + void genFIR(const Pa::CompilerDirective &) { TODO(); } + void genFIR(const Pa::OpenMPConstruct &) { TODO(); } + void genFIR(const Pa::OmpEndLoopDirective &) { TODO(); } + + void genFIR(const parser::AssociateStmt &) { TODO(); } + void genFIR(const parser::EndAssociateStmt &) { TODO(); } + void genFIR(const parser::BlockStmt &) { TODO(); } + void genFIR(const parser::EndBlockStmt &) { TODO(); } + void genFIR(const parser::SelectCaseStmt &) { TODO(); } + void genFIR(const parser::CaseStmt &) { TODO(); } + void genFIR(const parser::EndSelectStmt &) { TODO(); } + void genFIR(const parser::ChangeTeamStmt &) { TODO(); } + void genFIR(const parser::EndChangeTeamStmt &) { TODO(); } + void genFIR(const parser::CriticalStmt &) { TODO(); } + void genFIR(const parser::EndCriticalStmt &) { TODO(); } + void genFIR(const parser::NonLabelDoStmt &) { TODO(); } + void genFIR(const parser::EndDoStmt &) { TODO(); } + + // If-Then-Else is handled by genFIREvalCondGoto() + void genFIR(const parser::IfThenStmt &) {} /* do nothing */ + void genFIR(const parser::ElseIfStmt &) {} /* do nothing */ + void genFIR(const parser::ElseStmt &) {} /* do nothing */ + void genFIR(const parser::EndIfStmt &) {} /* do nothing */ + + void genFIR(const parser::SelectRankStmt &) { TODO(); } + void genFIR(const parser::SelectRankCaseStmt &) { TODO(); } + void genFIR(const parser::SelectTypeStmt &) { TODO(); } + void genFIR(const parser::TypeGuardStmt &) { TODO(); } + void genFIR(const parser::WhereConstructStmt &) { TODO(); } + void genFIR(const parser::MaskedElsewhereStmt &) { TODO(); } + void genFIR(const parser::ElsewhereStmt &) { TODO(); } + void genFIR(const parser::EndWhereStmt &) { TODO(); } + void genFIR(const parser::ForallConstructStmt &) { TODO(); } + void genFIR(const parser::EndForallStmt &) { TODO(); } + + // + // Statements that do not have control-flow semantics + // + + void genFIR(const Pa::AllocateStmt &) { TODO(); } + void genFIR(const Pa::AssignmentStmt &stmt) { + auto *rhs{Se::GetExpr(std::get(stmt.t))}; + auto *lhs{Se::GetExpr(std::get(stmt.t))}; + auto loc{toLocation()}; + builder->create( + loc, createFIRExpr(loc, rhs), createFIRAddr(loc, lhs)); } + void genFIR(const Pa::BackspaceStmt &) { TODO(); } + + void genFIR(const Pa::CloseStmt &) { TODO(); } + void genFIR(const Pa::ContinueStmt &) { TODO(); } + void genFIR(const Pa::DeallocateStmt &) { TODO(); } + void genFIR(const Pa::EndfileStmt &) { TODO(); } + void genFIR(const Pa::EventPostStmt &) { TODO(); } + void genFIR(const Pa::EventWaitStmt &) { TODO(); } + void genFIR(const Pa::FlushStmt &) { TODO(); } + void genFIR(const Pa::FormTeamStmt &) { TODO(); } + void genFIR(const Pa::InquireStmt &) { TODO(); } + void genFIR(const Pa::LockStmt &) { TODO(); } + void genFIR(const Pa::NullifyStmt &) { TODO(); } + void genFIR(const Pa::OpenStmt &) { TODO(); } + void genFIR(const Pa::PointerAssignmentStmt &) { TODO(); } - void genEnterFIR(const Pa::AssociateConstruct &) { TODO(); } - void genEnterFIR(const Pa::BlockConstruct &) { TODO(); } - void genEnterFIR(const Pa::CaseConstruct &) { TODO(); } - void genEnterFIR(const Pa::ChangeTeamConstruct &) { TODO(); } - void genEnterFIR(const Pa::CriticalConstruct &) { TODO(); } - void genEnterFIR(const Pa::IfConstruct &) { TODO(); } - void genEnterFIR(const Pa::CompilerDirective &) { TODO(); } - void genEnterFIR(const Pa::OpenMPConstruct &) { TODO(); } - void genEnterFIR(const Pa::OmpEndLoopDirective &) { TODO(); } - void genEnterFIR(const Pa::SelectRankConstruct &) { TODO(); } - void genEnterFIR(const Pa::SelectTypeConstruct &) { TODO(); } - - void genExitFIR(const Pa::DoConstruct &construct) { - if (firLoopOp) { - build().setInsertionPointAfter( - build().getBlock()->getParent()->getParentOp()); - return; - } - auto &stmt{std::get>(construct.t)}; - setCurrentPos(stmt.source); - const Pa::NonLabelDoStmt &ss{stmt.statement}; - auto &ctrl{std::get>(ss.t)}; - if (ctrl.has_value() && - std::holds_alternative(ctrl->u)) { - doMap.erase(&ss); + void genFIR(const Pa::PrintStmt &stmt) { + L::SmallVector args; + for (auto &item : std::get>(stmt.t)) { + if (auto *parserExpr{std::get_if(&item.u)}) { + auto loc{toLocation(parserExpr->source)}; + args.push_back(createFIRExpr(loc, Se::GetExpr(*parserExpr))); + } else { + TODO(); // implied do + } } - noInsPt = true; // backedge already processed + genPrintStatement(*builder, toLocation(), args); + } + + void genFIR(const Pa::ReadStmt &) { TODO(); } + void genFIR(const Pa::RewindStmt &) { TODO(); } + void genFIR(const Pa::SyncAllStmt &) { TODO(); } + void genFIR(const Pa::SyncImagesStmt &) { TODO(); } + void genFIR(const Pa::SyncMemoryStmt &) { TODO(); } + void genFIR(const Pa::SyncTeamStmt &) { TODO(); } + void genFIR(const Pa::UnlockStmt &) { TODO(); } + + void genFIR(const Pa::WriteStmt &) { TODO(); } + void genFIR(const Pa::AssignStmt &) { TODO(); } + void genFIR(const Pa::FormatStmt &) { TODO(); } + void genFIR(const Pa::EntryStmt &) { TODO(); } + void genFIR(const Pa::PauseStmt &) { TODO(); } + void genFIR(const Pa::DataStmt &) { TODO(); } + void genFIR(const Pa::NamelistStmt &) { TODO(); } + + // call FAIL IMAGE in runtime + void genFIR(const Pa::FailImageStmt &stmt) { + auto callee{genRuntimeFunction(FIRT_FAIL_IMAGE, 0)}; + L::SmallVector operands; // FAIL IMAGE has no args + builder->create(toLocation(), callee, operands); } - void genFIR(const Fl::EndOp &op) { - if (auto *construct{std::get_if(&op.u)}) - genExitFIR(**construct); + // call STOP, ERROR STOP in runtime + void genFIR(const Pa::StopStmt &stm) { + auto callee{ + genRuntimeFunction(isStopStmt(stm) ? FIRT_STOP : FIRT_ERROR_STOP, + defaults.GetDefaultKind(IntegerCat))}; + // 2 args: stop-code-opt, quiet-opt + L::SmallVector operands; + builder->create(toLocation(), callee, operands); } - void genFIR(AnalysisData &ad, const Fl::IndirectGotoOp &op); - - void genFIR(const Fl::DoIncrementOp &op) { - if (firLoopOp) { + // gen expression, if any + void genFIR(const Pa::ReturnStmt &stmt) { + if (inMainProgram(currentEvaluation)) { + builder->create(toLocation()); return; } - auto *info{getBoundsInfo(op)}; - if (info->doVar && info->stepExpr) { - // add: do_var = do_var + e3 - auto load{ - build().create(info->doVar->getLoc(), info->doVar)}; - auto incremented{build().create( - load.getLoc(), load.getResult(), info->stepExpr)}; - build().create(load.getLoc(), incremented, info->doVar); - // add: counter-- - auto loadCtr{ - build().create(info->counter->getLoc(), info->counter)}; - auto one{build().create( - loadCtr.getLoc(), build().getIntegerAttr(loadCtr.getType(), 1))}; - auto decremented{build().create( - loadCtr.getLoc(), loadCtr.getResult(), one)}; - build().create( - loadCtr.getLoc(), decremented, info->counter); + if (auto *stmt = inSubroutine(currentEvaluation)) { + genFIRProcedureExit(stmt); + return; } - } - - void genFIR(const Fl::DoCompareOp &op) { - if (firLoopOp) { + if (auto *stmt = inFunction(currentEvaluation)) { + genFIRFunctionReturn(stmt); return; } - auto *info{getBoundsInfo(op)}; - if (info->doVar && info->stepExpr) { - // add: cond = counter > 0 (signed) - auto load{ - build().create(info->counter->getLoc(), info->counter)}; - auto zero{build().create( - load.getLoc(), build().getIntegerAttr(load.getType(), 0))}; - auto cond{build().create( - load.getLoc(), M::CmpIPredicate::sgt, load, zero)}; - info->condition = cond; + if (auto *stmt = inMPSubp(currentEvaluation)) { + genFIRProcedureExit(stmt); + return; } + assert(false && "unknown subprogram type"); } - void genFIR(const Pa::FailImageStmt &stmt) { - auto callee{genRuntimeFunction(FIRT_FAIL_IMAGE, 0)}; - llvm::SmallVector operands; // FAIL IMAGE has no args - build().create(toLocation(), callee, operands); - build().create(toLocation()); - } - - void genReturnStmt(AnalysisData &, const Pa::FunctionSubprogram &func) { - auto &stmt{std::get>(func.t)}; - auto &name{std::get(stmt.statement.t)}; - assert(name.symbol); - const auto &details{name.symbol->get()}; - M::Value *resultRef{symbolMap.lookupSymbol(details.result())}; - assert(resultRef); // FIXME might die if result - // was never referenced before and temp not created. - M::Value *resultVal{build().create(toLocation(), resultRef)}; - build().create(toLocation(), resultVal); - } + // stubs for generic goto statements; see genFIREvalGoto + void genFIR(const Pa::CycleStmt &) { assert(false && "invalid"); } + void genFIR(const Pa::ExitStmt &) { assert(false && "invalid"); } + void genFIR(const Pa::GotoStmt &) { assert(false && "invalid"); } - void genReturnStmt(const Pa::MainProgram &) { - build().create(toLocation()); - } - - void genReturnStmt( - const Pa::SubroutineSubprogram &, const Pa::ReturnStmt * = nullptr) { - // TODO use Pa::ReturnStmt for alternate return - build().create(toLocation()); - } - void genReturnStmt(AnalysisData &ad, const Pa::ReturnStmt *stmt = nullptr) { + void genFIR(AST::Evaluation &eval) { + currentEvaluation = &eval; std::visit(Co::visitors{ - [&](const Pa::SubroutineSubprogram *sub) { - genReturnStmt(*sub, stmt); - }, - [&](const Pa::FunctionSubprogram *func) { - genReturnStmt(ad, *func); - }, - [&](const Pa::MainProgram *main) { genReturnStmt(*main); }, + [&](const auto *p) { genFIR(*p); }, + [](const AST::CGJump &) { assert(false && "invalid"); }, }, - ad.parseTreeRoot); + eval.u); } - void genFIR(const Pa::StopStmt &stmt) { - auto callee{genRuntimeFunction( - isStopStmt(std::get(stmt.t)) ? FIRT_STOP - : FIRT_ERROR_STOP, - defaults.GetDefaultKind(IntegerCat))}; - // 2 args: stop-code-opt, quiet-opt - llvm::SmallVector operands; - build().create(toLocation(), callee, operands); - build().create(toLocation()); + void lowerEval(AST::Evaluation &eval) { + setCurrentPosition(eval.pos); + if (eval.isControlTarget()) { + // start a new block + } + switch (eval.cfg) { + case AST::CFGAnnotation::None: genFIREvalNone(eval); break; + case AST::CFGAnnotation::Goto: genFIREvalGoto(eval); break; + case AST::CFGAnnotation::CondGoto: genFIREvalCondGoto(eval); break; + case AST::CFGAnnotation::IndGoto: genFIREvalIndGoto(eval); break; + case AST::CFGAnnotation::IoSwitch: genFIREvalIoSwitch(eval); break; + case AST::CFGAnnotation::Switch: genFIREvalSwitch(eval); break; + case AST::CFGAnnotation::Iterative: genFIREvalIterative(eval); break; + case AST::CFGAnnotation::Return: genFIREvalReturn(eval); break; + case AST::CFGAnnotation::Terminate: genFIREvalTerminate(eval); break; + } } - // Conditional branch-like statements - template - void genFIR( - const A &tuple, Fl::LabelMention trueLabel, Fl::LabelMention falseLabel) { - auto *exprRef{Se::GetExpr(std::get(tuple))}; - assert(exprRef && "condition expression missing"); - auto *cond{createLogicalExprAsI1(toLocation(), exprRef)}; - genCondBranch(cond, trueLabel, falseLabel); - } - void genFIR(const Pa::Statement &stmt, - Fl::LabelMention trueLabel, Fl::LabelMention falseLabel) { - setCurrentPos(stmt.source); - genFIR(stmt.statement.t, trueLabel, falseLabel); - } - void genFIR(const Pa::Statement &stmt, - Fl::LabelMention trueLabel, Fl::LabelMention falseLabel) { - setCurrentPos(stmt.source); - genFIR(stmt.statement.t, trueLabel, falseLabel); - } - void genFIR(const Pa::IfStmt &stmt, Fl::LabelMention trueLabel, - Fl::LabelMention falseLabel) { - genFIR(stmt.t, trueLabel, falseLabel); - } - - M::Value *getTrueConstant() { - auto attr{build().getBoolAttr(true)}; - return build().create(toLocation(), attr).getResult(); - } - - // Conditional branch to enter loop body or exit - void genFIR(const Pa::Statement &stmt, - Fl::LabelMention trueLabel, Fl::LabelMention falseLabel) { - setCurrentPos(stmt.source); - auto &loopCtrl{std::get>(stmt.statement.t)}; - M::Value *condition{nullptr}; - bool exitNow{false}; - if (loopCtrl.has_value()) { - exitNow = std::visit( - Co::visitors{ - [&](const parser::LoopControl::Bounds &) { - if (firLoopOp) { - return true; - } - auto iter{doMap.find(&stmt.statement)}; - assert(iter != doMap.end()); - condition = iter->second.condition->getResult(0); - return false; - }, - [&](const parser::ScalarLogicalExpr &logical) { - auto loc{toLocation(stmt.source)}; - auto *exp{Se::GetExpr(logical)}; - condition = createLogicalExprAsI1(loc, exp); - return false; - }, - [&](const parser::LoopControl::Concurrent &concurrent) { - // FIXME: incorrectly lowering DO CONCURRENT - condition = getTrueConstant(); - return false; - }, - }, - loopCtrl->u); - if (firLoopOp && exitNow) { - return; + M::FuncOp createNewFunction(L::StringRef name, const Se::Symbol *symbol) { + // get arguments and return type if any, otherwise just use empty vectors + L::SmallVector args; + L::SmallVector results; + if (symbol) { + auto *details{symbol->detailsIf()}; + assert(details && "details for semantics::Symbol must be subprogram"); + for (auto *a : details->dummyArgs()) { + if (a) { // nullptr indicates alternate return argument + auto type{translateSymbolToFIRType(&mlirContext, defaults, *a)}; + args.push_back(fir::ReferenceType::get(type)); + } + } + if (details->isFunction()) { + // FIXME: handle subroutines that return magic values + auto result{details->result()}; + results.push_back( + translateSymbolToFIRType(&mlirContext, defaults, result)); } - } else { - condition = getTrueConstant(); } - assert(condition && "condition must be a Value"); - genCondBranch(condition, trueLabel, falseLabel); + auto funcTy{M::FunctionType::get(args, results, &mlirContext)}; + return createFunction(module, name, funcTy); } - // Action statements - void genFIR(const Pa::AllocateStmt &stmt) { TODO(); } - - void genFIR(const Pa::AssignmentStmt &stmt) { - auto *rhs{Se::GetExpr(std::get(stmt.t))}; - auto *lhs{Se::GetExpr(std::get(stmt.t))}; - auto loc{toLocation()}; - build().create( - loc, createFIRExpr(loc, rhs), createFIRAddr(loc, lhs)); - } - void genFIR(const Pa::BackspaceStmt &stmt) { TODO(); } - void genFIR(const Pa::CallStmt &stmt) { TODO(); } - void genFIR(const Pa::CloseStmt &stmt) { TODO(); } - void genFIR(const Pa::DeallocateStmt &stmt) { TODO(); } - void genFIR(const Pa::EndfileStmt &stmt) { TODO(); } - void genFIR(const Pa::EventPostStmt &stmt) { TODO(); } - void genFIR(const Pa::EventWaitStmt &stmt) { TODO(); } - void genFIR(const Pa::FlushStmt &stmt) { TODO(); } - void genFIR(const Pa::FormTeamStmt &stmt) { TODO(); } - void genFIR(const Pa::InquireStmt &stmt) { TODO(); } - void genFIR(const Pa::LockStmt &stmt) { TODO(); } - void genFIR(const Pa::NullifyStmt &stmt) { TODO(); } - void genFIR(const Pa::OpenStmt &stmt) { TODO(); } - void genFIR(const Pa::PointerAssignmentStmt &stmt) { TODO(); } - void genFIR(const Pa::PrintStmt &stmt) { - llvm::SmallVector args; - for (const Pa::OutputItem &item : - std::get>(stmt.t)) { - if (const Pa::Expr * parserExpr{std::get_if(&item.u)}) { - mlir::Location loc{toLocation(parserExpr->source)}; - args.push_back(createFIRExpr(loc, Se::GetExpr(*parserExpr))); - } else { - assert(false); // implied do TODO + /// Prepare to translate a new function + void startNewFunction(AST::FunctionLikeUnit &funit, L::StringRef name, + const Se::Symbol *symbol) { + M::FuncOp func{getNamedFunction(module, name)}; + if (!func) { + func = createNewFunction(name, symbol); + } + func.addEntryBlock(); + assert(!builder && "expected nullptr"); + builder = new M::OpBuilder(func); + assert(builder && "OpBuilder did not instantiate"); + builder->setInsertionPointToStart(&func.front()); + + // plumb function's arguments + if (symbol) { + auto *entryBlock{&func.front()}; + auto *details{symbol->detailsIf()}; + assert(details && "details for semantics::Symbol must be subprogram"); + for (const auto &v : + L::zip(details->dummyArgs(), entryBlock->getArguments())) { + if (std::get<0>(v)) { + localSymbols.addSymbol(*std::get<0>(v), std::get<1>(v)); + } else { + TODO(); // handle alternate return + } } } - genPrintStatement(build(), toLocation(lastKnownPos), args); - } - void genFIR(const Pa::ReadStmt &stmt) { TODO(); } - void genFIR(const Pa::RewindStmt &stmt) { TODO(); } - void genFIR(const Pa::SyncAllStmt &stmt) { TODO(); } - void genFIR(const Pa::SyncImagesStmt &stmt) { TODO(); } - void genFIR(const Pa::SyncMemoryStmt &stmt) { TODO(); } - void genFIR(const Pa::SyncTeamStmt &stmt) { TODO(); } - void genFIR(const Pa::UnlockStmt &stmt) { TODO(); } - void genFIR(const Pa::WaitStmt &stmt) { TODO(); } - void genFIR(const Pa::WhereStmt &stmt) { TODO(); } - void genFIR(const Pa::WriteStmt &stmt) { TODO(); } - void genFIR(const Pa::ForallStmt &stmt) { TODO(); } - void genFIR(AnalysisData &ad, const Pa::AssignStmt &stmt) { TODO(); } - void genFIR(const Pa::PauseStmt &stmt) { TODO(); } + } - template - void translateRoutine( - const A &routine, llvm::StringRef name, const Se::Symbol *funcSym); - - void genCondBranch( - M::Value *cond, Fl::LabelMention trueBlock, Fl::LabelMention falseBlock) { - auto trueIter{blkMap().find(trueBlock)}; - auto falseIter{blkMap().find(falseBlock)}; - if (trueIter != blkMap().end() && falseIter != blkMap().end()) { - llvm::SmallVector blanks; - build().create(toLocation(), cond, trueIter->second, - blanks, falseIter->second, blanks); - } else { - using namespace std::placeholders; - edgeQ.emplace_back(std::bind( - [](M::OpBuilder *builder, M::Block *block, M::Value *cnd, - Fl::LabelMention trueDest, Fl::LabelMention falseDest, - M::Location location, const LabelMapType &map) { - llvm::SmallVector blk; - builder->setInsertionPointToEnd(block); - auto tdp{map.find(trueDest)}; - auto fdp{map.find(falseDest)}; - assert(tdp != map.end() && fdp != map.end()); - builder->create( - location, cnd, tdp->second, blk, fdp->second, blk); - }, - &build(), build().getInsertionBlock(), cond, trueBlock, falseBlock, - toLocation(), _1)); + void finalizeQueuedEdges() { + for (auto &edgeFunc : localEdgeQ) { + edgeFunc(localBlockMap); } + localEdgeQ.clear(); + localBlockMap.clear(); } - template - void genSwitchBranch(const M::Location &loc, M::Value *selector, - std::list &&conditions, - const std::vector &labels) { - assert(conditions.size() == labels.size()); - bool haveAllLabels{true}; - std::size_t u{0}; - // do we already have all the targets? - for (auto last{labels.size()}; u != last; ++u) { - haveAllLabels = blkMap().find(labels[u]) != blkMap().end(); - if (!haveAllLabels) break; - } - if (haveAllLabels) { - // yes, so generate the FIR operation now - u = 0; - std::vector conds; - std::vector blocks; - std::vector> blockArgs; - llvm::SmallVector blanks; - for (auto cond : conditions) { - conds.emplace_back(cond); - blocks.emplace_back(blkMap().find(labels[u++])->second); - blockArgs.emplace_back(blanks); - } - build().create(loc, selector, conds, blocks, blockArgs); + /// Cleanup after the function has been translated + void endNewFunction() { + finalizeQueuedEdges(); + delete builder; + builder = nullptr; + localSymbols.clear(); + } + + /// Lower a procedure-like construct + void lowerFunc(AST::FunctionLikeUnit &func, L::ArrayRef modules, + L::Optional host = {}) { + std::string name; + const Se::Symbol *symbol{nullptr}; + auto size{func.funStmts.size()}; + + assert((size == 1 || size == 2) && "ill-formed subprogram"); + if (size == 2) { + std::visit( + [&](auto *p) { genFIR(*p, name, symbol); }, func.funStmts.front()); } else { - // no, so queue the FIR operation for later - using namespace std::placeholders; - edgeQ.emplace_back(std::bind( - [](M::OpBuilder *builder, M::Block *block, M::Value *sel, - const std::list &conditions, - const std::vector &labels, M::Location location, - const LabelMapType &map) { - std::size_t u{0}; - std::vector conds; - std::vector blocks; - std::vector> blockArgs; - llvm::SmallVector blanks; - for (auto &cond : conditions) { - auto iter{map.find(labels[u++])}; - assert(iter != map.end()); - conds.emplace_back(cond); - blocks.emplace_back(iter->second); - blockArgs.emplace_back(blanks); - } - builder->setInsertionPointToEnd(block); - builder->create(location, sel, conds, blocks, blockArgs); - }, - &build(), build().getInsertionBlock(), selector, conditions, labels, - loc, _1)); + name = mangler.getProgramEntry(); } - } - void finalizeQueued() { - for (auto &edgeFunc : edgeQ) { - edgeFunc(blkMap()); + startNewFunction(func, name, symbol); + + // lower this procedure + for (auto &e : func.evals) { + lowerEval(e); } - } + std::visit( + [&](auto *p) { genFIR(*p, name, symbol); }, func.funStmts.back()); -public: - FIRConverter(BurnsideBridge &bridge) - : mlirContext{bridge.getMLIRContext()}, cooked{bridge.getCookedSource()}, - module{bridge.getModule()}, defaults{bridge.getDefaultKinds()}, - intrinsics{IntrinsicLibrary::create( - IntrinsicLibrary::Version::LLVM, mlirContext)} {} - FIRConverter() = delete; + endNewFunction(); - template constexpr bool Pre(const A &) { return true; } - template constexpr void Post(const A &) { - // FIXME: make sure we lower all of the parse tree + // recursively lower internal procedures + L::Optional optName{name}; + for (auto &f : func.funcs) { + lowerFunc(f, modules, optName); + } } - /// Translate the various routines from the parse tree - void Post(const Pa::MainProgram &mainp) { - std::string mainName{"_MAIN"s}; - if (auto &ps{ - std::get>>(mainp.t)}) { - mainName = ps->statement.v.ToString(); - setCurrentPos(ps->source); + void lowerMod(AST::ModuleLikeUnit &mod) { + // FIXME: build the vector of module names + std::vector moduleName; + + // FIXME: do we need to visit the module statements? + for (auto &f : mod.funcs) { + lowerFunc(f, moduleName); } - translateRoutine(mainp, mainName, nullptr); } - void Post(const Pa::FunctionSubprogram &subp) { - auto &stmt{std::get>(subp.t)}; - setCurrentPos(stmt.source); - auto &name{std::get(stmt.statement.t)}; - translateRoutine(subp, name.ToString(), name.symbol); + + // + // Finalization of the CFG structure + // + + /// Lookup the set of sinks for this source. There must be at least one. + L::ArrayRef findTargetsOf(AST::Evaluation &eval) { + auto iter = cfgMap.find(&eval); + assert(iter != cfgMap.end()); + return *iter->second; } - void Post(const Pa::SubroutineSubprogram &subp) { - auto &stmt{std::get>(subp.t)}; - setCurrentPos(stmt.source); - auto &name{std::get(stmt.statement.t)}; - translateRoutine(subp, name.ToString(), name.symbol); + + /// Lookup the sink for this source. There must be exactly one. + AST::Evaluation *findSinkOf(AST::Evaluation &eval) { + auto iter = cfgMap.find(&eval); + assert((iter != cfgMap.end()) && (iter->second->size() == 1)); + return iter->second->front(); } -}; -/// SELECT CASE -/// Build a switch-like structure for a SELECT CASE -void FIRConverter::genFIR( - const Fl::SwitchOp &op, const Pa::CaseConstruct &stmt) { - auto loc{toLocation(op.source)}; - auto &cstm{std::get>(stmt.t)}; - auto *exp{Se::GetExpr(std::get>(cstm.statement.t))}; - auto *e1{createFIRExpr(loc, exp)}; - auto &cases{std::get>(stmt.t)}; - std::list conds; - // Per C1145, we know each `case-expr` must have type INTEGER, CHARACTER, or - // LOGICAL - for (auto &sel : cases) { - auto &cs{std::get>(sel.t)}; - auto locs{toLocation(cs.source)}; - auto &csel{std::get(cs.statement.t)}; - std::visit( - Co::visitors{ - [&](const std::list &ranges) { - for (auto &r : ranges) { - std::visit(Co::visitors{ - [&](const Pa::CaseValue &val) { - auto *term{fromCaseValue(locs, val)}; - conds.emplace_back(genEQ(e1, term)); - }, - [&](const Pa::CaseValueRange::Range &rng) { - fir::SelectCaseOp::Conditions rangeComparison = - nullptr; - if (rng.lower.has_value()) { - auto *term{fromCaseValue(locs, *rng.lower)}; - // rc = e1 >= lower.term - rangeComparison = genGE(e1, term); - } - if (rng.upper.has_value()) { - auto *term{fromCaseValue(locs, *rng.upper)}; - // c = e1 <= upper.term - auto *comparison{genLE(e1, term)}; - // rc = if rc then (rc && c) else c - if (rangeComparison) { - rangeComparison = - genAND(rangeComparison, comparison); - } else { - rangeComparison = comparison; - } - } - conds.emplace_back(rangeComparison); - }, - }, - r.u); - } - }, - [&](const Pa::Default &) { conds.emplace_back(getTrueConstant()); }, - }, - csel.u); + /// prune the CFG for `f` + void pruneFunc(AST::FunctionLikeUnit &func) { + // find and cache arcs, etc. + if (!func.evals.empty()) { + CfgBuilder{cfgMap, cfgEdgeSetPool}.run(func); + } + + // do any internal procedures + for (auto &f : func.funcs) { + pruneFunc(f); + } } - genSwitchBranch(loc, e1, std::move(conds), op.refs); -} -/// SELECT RANK -/// Build a switch-like structure for a SELECT RANK -void FIRConverter::genFIR( - const Fl::SwitchOp &op, const Pa::SelectRankConstruct &stmt) { - auto loc{toLocation(op.source)}; - auto &rstm{std::get>(stmt.t)}; - auto *exp{std::visit([](auto &x) { return Se::GetExpr(x); }, - std::get(rstm.statement.t).u)}; - auto *e1{createFIRExpr(loc, exp)}; - auto &ranks{std::get>(stmt.t)}; - std::list conds; - for (auto &r : ranks) { - auto &rs{std::get>(r.t)}; - auto &rank{std::get(rs.statement.t)}; - std::visit( - Co::visitors{ - [&](const Pa::ScalarIntConstantExpr &ex) { - auto *ie{createFIRExpr(loc, Se::GetExpr(ex))}; - conds.emplace_back(ie); - }, - [&](const Pa::Star &) { - // FIXME: using a bogon for now. Special value per - // whatever the runtime returns. - auto attr{build().getIntegerAttr(e1->getType(), -1)}; - conds.emplace_back(build().create(loc, attr)); - }, - [&](const Pa::Default &) { conds.emplace_back(getTrueConstant()); }, - }, - rank.u); - } - // FIXME: fix the type of the function - auto callee{genRuntimeFunction(FIRT_GET_RANK, 0)}; - llvm::SmallVector operands{e1}; - auto e3{build().create(loc, callee, operands)}; - genSwitchBranch( - loc, e3.getResult(0), std::move(conds), op.refs); -} + void pruneMod(AST::ModuleLikeUnit &mod) { + for (auto &f : mod.funcs) { + pruneFunc(f); + } + } -/// SELECT TYPE -/// Build a switch-like structure for a SELECT TYPE -void FIRConverter::genFIR( - const Fl::SwitchOp &op, const Pa::SelectTypeConstruct &stmt) { - auto loc{toLocation(op.source)}; - auto &tstm{std::get>(stmt.t)}; - auto *exp{std::visit([](auto &x) { return Se::GetExpr(x); }, - std::get(tstm.statement.t).u)}; - auto *e1{createFIRExpr(loc, exp)}; - auto &types{std::get>(stmt.t)}; - std::list conds; - for (auto &t : types) { - auto &ts{std::get>(t.t)}; - auto &ty{std::get(ts.statement.t)}; - std::visit( - Co::visitors{ - [&](const Pa::TypeSpec &) { - // FIXME: add arguments - auto func{genRuntimeFunction(FIRT_ISA_TYPE, 0)}; - llvm::SmallVector operands; - auto call{build().create(loc, func, operands)}; - conds.emplace_back(call.getResult(0)); - }, - [&](const Pa::DerivedTypeSpec &) { - // FIXME: add arguments - auto func{genRuntimeFunction(FIRT_ISA_SUBTYPE, 0)}; - llvm::SmallVector operands; - auto call{build().create(loc, func, operands)}; - conds.emplace_back(call.getResult(0)); - }, - [&](const Pa::Default &) { conds.emplace_back(getTrueConstant()); }, - }, - ty.u); + void setCurrentPosition(const Pa::CharBlock &pos) { + if (pos != Pa::CharBlock{}) { + currentPosition = pos; + } } - auto callee{genRuntimeFunction(FIRT_GET_ELETYPE, 0)}; - llvm::SmallVector operands{e1}; - auto e3{build().create(loc, callee, operands)}; - genSwitchBranch( - loc, e3.getResult(0), std::move(conds), op.refs); -} -/// translate action statements -void FIRConverter::genFIR(AnalysisData &ad, const Fl::ActionOp &op) { - setCurrentPos(op.v->source); - std::visit(Co::visitors{ - [](const Pa::ContinueStmt &) { TODO(); }, - [](const Pa::FailImageStmt &) { TODO(); }, - [](const Co::Indirection &) { TODO(); }, - [](const Co::Indirection &) { TODO(); }, - [](const Co::Indirection &) { TODO(); }, - [](const Co::Indirection &) { TODO(); }, - [](const Co::Indirection &) { TODO(); }, - [](const Co::Indirection &) { TODO(); }, - [](const Co::Indirection &) { TODO(); }, - [](const Co::Indirection &) { TODO(); }, - [&](const Co::Indirection &assign) { - genFIR(ad, assign.value()); - }, - [](const Co::Indirection &) { - assert(false && "should be a ReturnOp"); - }, - [&](const auto &stmt) { genFIR(stmt); }, - }, - op.v->statement.u); -} + // + // Utility methods + // -void FIRConverter::genFIR(AnalysisData &ad, const Fl::IndirectGotoOp &op) { - // add or queue an igoto - TODO(); -} + /// Convert a parser CharBlock to a Location + M::Location toLocation(const Pa::CharBlock &cb) { + return parserPosToLoc(mlirContext, cooked, cb); + } -void FIRConverter::genFIR(AnalysisData &ad, std::list &operations) { - bool lastWasLabel{false}; - for (auto &op : operations) { - std::visit(Co::visitors{ - [&](const Fl::IndirectGotoOp &oper) { - genFIR(ad, oper); - lastWasLabel = false; - }, - [&](const Fl::ActionOp &oper) { - noInsPt = false; - genFIR(ad, oper); - lastWasLabel = false; - }, - [&](const Fl::LabelOp &oper) { - genFIR(lastWasLabel, oper); - lastWasLabel = true; - }, - [&](const Fl::BeginOp &oper) { - noInsPt = false; - genFIR(oper); - lastWasLabel = true; - }, - [&](const Fl::ReturnOp &oper) { - noInsPt = false; - genFIR(ad, oper); - lastWasLabel = true; - }, - [&](const auto &oper) { - noInsPt = false; - genFIR(oper); - lastWasLabel = false; - }, - }, - op.u); + M::Location toLocation() { return toLocation(currentPosition); } + + // TODO: should these be moved to convert-expr? + template + M::Value *genCompare(M::Value *lhs, M::Value *rhs) { + auto lty{lhs->getType()}; + assert(lty == rhs->getType()); + if (lty.isIntOrIndex()) { + return builder->create(lhs->getLoc(), ICMPOPC, lhs, rhs); + } + if (fir::LogicalType::kindof(lty.getKind())) { + return builder->create(lhs->getLoc(), ICMPOPC, lhs, rhs); + } + if (fir::CharacterType::kindof(lty.getKind())) { + // FIXME + // return builder->create(lhs->getLoc(), ); + } + assert(false && "cannot generate operation on this type"); + return {}; + } + M::Value *genGE(M::Value *lhs, M::Value *rhs) { + return genCompare(lhs, rhs); } - if (build().getInsertionBlock()) { - genReturnStmt(ad); + M::Value *genLE(M::Value *lhs, M::Value *rhs) { + return genCompare(lhs, rhs); + } + M::Value *genEQ(M::Value *lhs, M::Value *rhs) { + return genCompare(lhs, rhs); + } + M::Value *genAND(M::Value *lhs, M::Value *rhs) { + return builder->create(lhs->getLoc(), lhs, rhs); } -} -/// Translate the routine to MLIR -template -void FIRConverter::translateRoutine( - const A &routine, llvm::StringRef name, const Se::Symbol *funcSym) { - M::FuncOp func{getNamedFunction(getMod(), name)}; - if (!func) { - // get arguments and return type if any, otherwise just use empty vectors - llvm::SmallVector args; - llvm::SmallVector results; - if (funcSym) { - if (auto *details{funcSym->detailsIf()}) { - for (auto *a : details->dummyArgs()) { - if (a) { // nullptr indicates alternate return argument - auto type{translateSymbolToFIRType(&mlirContext, defaults, *a)}; - args.push_back(fir::ReferenceType::get(type)); - } - } - if (details->isFunction()) { - // FIXME: handle subroutines that return magic values - auto result{details->result()}; - results.push_back( - translateSymbolToFIRType(&mlirContext, defaults, result)); - } - } else { - llvm::errs() << "Symbol: " << funcSym->name().ToString() << " @ " - << funcSym->details().index() << '\n'; - assert(false && "symbol misidentified by front-end"); - } +private: + M::MLIRContext &mlirContext; + const Pa::CookedSource *cooked; + M::ModuleOp &module; + Co::IntrinsicTypeDefaultKinds const &defaults; + IntrinsicLibrary intrinsics; + M::OpBuilder *builder{nullptr}; + fir::NameMangler &mangler; + SymMap localSymbols; + std::list localEdgeQ; + LabelMapType localBlockMap; + Pa::CharBlock currentPosition; + CFGMapType cfgMap; + std::list cfgEdgeSetPool; + SymMap symbolMap; + AST::Evaluation *currentEvaluation; // FIXME: this is a hack + +public: + FirConverter() = delete; + FirConverter(const FirConverter &) = delete; + FirConverter &operator=(const FirConverter &) = delete; + + explicit FirConverter(BurnsideBridge &bridge, fir::NameMangler &mangler) + : mlirContext{bridge.getMLIRContext()}, cooked{bridge.getCookedSource()}, + module{bridge.getModule()}, defaults{bridge.getDefaultKinds()}, + intrinsics{IntrinsicLibrary::create( + IntrinsicLibrary::Version::LLVM, bridge.getMLIRContext())}, + mangler{mangler} {} + + /// Convert the AST to FIR + void run(AST::Program &ast) { + // build pruned control + for (auto &u : ast.getUnits()) { + std::visit(common::visitors{ + [&](AST::FunctionLikeUnit &f) { pruneFunc(f); }, + [&](AST::ModuleLikeUnit &m) { pruneMod(m); }, + [](AST::BlockDataUnit &) { /* do nothing */ }, + }, + u); } - auto funcTy{M::FunctionType::get(args, results, &mlirContext)}; - func = createFunction(getMod(), name, funcTy); - } - func.addEntryBlock(); - builder = std::make_unique(func); - build().setInsertionPointToStart(&func.front()); - if (funcSym) { - auto *entryBlock{&func.front()}; - if (auto *details{funcSym->detailsIf()}) { - // TODO zipping might be an issue in case of alternate returns - for (const auto &v : - llvm::zip(details->dummyArgs(), entryBlock->getArguments())) { - if (std::get<0>(v)) { - symbolMap.addSymbol(*std::get<0>(v), std::get<1>(v)); - } else { - TODO(); // handle alternate return, maybe nothing todo here though - } - } - } else { - llvm::errs() << "Symbol: " << funcSym->name().ToString() << " @ " - << funcSym->details().index() << '\n'; - assert(false && "symbol misidentified by front-end"); + + // do translation + for (auto &u : ast.getUnits()) { + std::visit(common::visitors{ + [&](AST::FunctionLikeUnit &f) { lowerFunc(f, {}); }, + [&](AST::ModuleLikeUnit &m) { lowerMod(m); }, + [](AST::BlockDataUnit &) { SOFT_TODO(); }, + }, + u); } } - AnalysisData ad{routine}; - std::list operations; - CreateFlatIR(operations, ad); - genFIR(ad, operations); - finalizeQueued(); -} +}; } // namespace void Br::BurnsideBridge::lower( const Pa::Program &prg, fir::NameMangler &mangler) { - FIRConverter converter{*this}; - Walk(prg, converter); + AST::Program *ast{Br::createAST(prg)}; + Br::annotateControl(*ast); + FirConverter converter{*this, mangler}; + converter.run(*ast); + delete ast; } -void Br::BurnsideBridge::parseSourceFile(llvm::SourceMgr &srcMgr) { +void Br::BurnsideBridge::parseSourceFile(L::SourceMgr &srcMgr) { auto owningRef = M::parseSourceFile(srcMgr, context.get()); module.reset(new M::ModuleOp(owningRef.get().getOperation())); owningRef.release(); diff --git a/lib/burnside/builder.h b/lib/burnside/builder.h index 563a13e0db6f..d340652840be 100644 --- a/lib/burnside/builder.h +++ b/lib/burnside/builder.h @@ -34,6 +34,7 @@ class CookedSource; namespace burnside { /// Miscellaneous helper routines for building MLIR +/// /// [Coding style](https://llvm.org/docs/CodingStandards.html) class SymMap { @@ -43,6 +44,8 @@ class SymMap { void addSymbol(semantics::SymbolRef symbol, mlir::Value *value); mlir::Value *lookupSymbol(semantics::SymbolRef symbol); + + void clear() { symbolMap.clear(); } }; std::string applyNameMangling(llvm::StringRef parserName); diff --git a/lib/burnside/convert-type.h b/lib/burnside/convert-type.h index 2c4f7b94741b..05e570082a96 100644 --- a/lib/burnside/convert-type.h +++ b/lib/burnside/convert-type.h @@ -15,11 +15,11 @@ #ifndef FORTRAN_BURNSIDE_CONVERT_TYPE_H_ #define FORTRAN_BURNSIDE_CONVERT_TYPE_H_ -/// Traversal and conversion of Fortran type data structures into the FIR -/// dialect of MLIR. -/// -/// Lowering of parse tree TYPE, KIND, ATTRIBUTE information to the FIR type -/// system. +/// Conversion of front-end TYPE, KIND, ATTRIBUTE (TKA) information to FIR/MLIR. +/// This is meant to be the single point of truth (SPOT) for all type +/// conversions when lowering to FIR. This implements all lowering of parse +/// tree TKA to the FIR type system. If one is converting front-end types and +/// not using one of the routines provided here, it's being done wrong. /// /// [Coding style](https://llvm.org/docs/CodingStandards.html) From e483ea63532274dbdfa1a3cd3ae1a0345d612630 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 5 Dec 2019 12:01:49 -0800 Subject: [PATCH 053/123] remove flattened code --- lib/burnside/flattened.cc | 688 -------------------------------------- lib/burnside/flattened.h | 235 ------------- 2 files changed, 923 deletions(-) delete mode 100644 lib/burnside/flattened.cc delete mode 100644 lib/burnside/flattened.h diff --git a/lib/burnside/flattened.cc b/lib/burnside/flattened.cc deleted file mode 100644 index 54c0a9e8cc01..000000000000 --- a/lib/burnside/flattened.cc +++ /dev/null @@ -1,688 +0,0 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "flattened.h" -#include "../parser/parse-tree-visitor.h" -#include "../semantics/symbol.h" - -namespace Fortran::burnside { -namespace flat { - -// Labels are numbered [0 .. `n`] consecutively. They are unsigned. Not all -// labels are numbered. The unnumbered ones are given the value UINT_MAX. `n` -// should never approach UINT_MAX. -LabelBuilder::LabelBuilder() : referenced(32), counter{0u} {} - -LabelMention LabelBuilder::getNext() { - LabelMention next{counter++}; - auto cap{referenced.size()}; - if (cap < counter) { - referenced.resize(2 * cap); - } - referenced.reset(next); - return next; -} - -void LabelBuilder::setReferenced(LabelMention label) { - CHECK(label < referenced.getBitCapacity()); - referenced.set(label); -} - -bool LabelBuilder::isReferenced(LabelMention label) const { - CHECK(label < referenced.getBitCapacity()); - return referenced.test(label); -} - -LabelOp::LabelOp(LabelBuilder &builder) - : builder{builder}, label{builder.getNext()} {} - -LabelOp::LabelOp(const LabelOp &that) - : builder{that.builder}, label{that.label} {} - -LabelOp &LabelOp::operator=(const LabelOp &that) { - CHECK(&builder == &that.builder); - label = that.label; - return *this; -} - -void LabelOp::setReferenced() const { builder.setReferenced(label); } - -bool LabelOp::isReferenced() const { return builder.isReferenced(label); } - -static void AddAssign(AnalysisData &ad, const semantics::Symbol *symbol, - const parser::Label &label) { - ad.assignMap[symbol].insert(label); -} - -std::vector GetAssign( - AnalysisData &ad, const semantics::Symbol *symbol) { - std::vector result; - for (auto lab : ad.assignMap[symbol]) { - result.emplace_back(lab); - } - return result; -} - -static std::tuple FindStack( - const std::vector< - std::tuple> &stack, - const parser::Name *key) { - for (auto iter{stack.rbegin()}, iend{stack.rend()}; iter != iend; ++iter) { - if (std::get<0>(*iter) == key) { - return *iter; - } - } - assert(false && "construct name not on stack"); - return {}; -} - -LabelOp FetchLabel(AnalysisData &ad, const parser::Label &label) { - auto iter{ad.labelMap.find(label)}; - if (iter == ad.labelMap.end()) { - LabelOp ll{ad.labelBuilder}; - ll.setReferenced(); - ad.labelMap.insert({label, ll}); - return ll; - } - return iter->second; -} - -static LabelOp BuildNewLabel(AnalysisData &ad) { - return LabelOp{ad.labelBuilder}; -} - -template parser::Label GetErr(const A &stmt) { - if constexpr (std::is_same_v || - std::is_same_v) { - for (const auto &control : stmt.controls) { - if (std::holds_alternative(control.u)) { - return std::get(control.u).v; - } - } - } - if constexpr (std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v) { - for (const auto &spec : stmt.v) { - if (std::holds_alternative(spec.u)) { - return std::get(spec.u).v; - } - } - } - if constexpr (std::is_same_v) { - for (const auto &spec : std::get>(stmt.u)) { - if (std::holds_alternative(spec.u)) { - return std::get(spec.u).v; - } - } - } - return 0; -} - -template parser::Label GetEor(const A &stmt) { - if constexpr (std::is_same_v || - std::is_same_v) { - for (const auto &control : stmt.controls) { - if (std::holds_alternative(control.u)) { - return std::get(control.u).v; - } - } - } - if constexpr (std::is_same_v) { - for (const auto &waitSpec : stmt.v) { - if (std::holds_alternative(waitSpec.u)) { - return std::get(waitSpec.u).v; - } - } - } - return 0; -} - -template parser::Label GetEnd(const A &stmt) { - if constexpr (std::is_same_v || - std::is_same_v) { - for (const auto &control : stmt.controls) { - if (std::holds_alternative(control.u)) { - return std::get(control.u).v; - } - } - } - if constexpr (std::is_same_v) { - for (const auto &waitSpec : stmt.v) { - if (std::holds_alternative(waitSpec.u)) { - return std::get(waitSpec.u).v; - } - } - } - return 0; -} - -template -void errLabelSpec(const A &s, std::list &ops, - const parser::Statement &ec, AnalysisData &ad) { - if (auto errLab{GetErr(s)}) { - std::optional errRef{FetchLabel(ad, errLab).get()}; - LabelOp next{BuildNewLabel(ad)}; - ops.emplace_back(SwitchIOOp{s, next, ec.source, errRef}); - ops.emplace_back(next); - } else { - ops.emplace_back(ActionOp{ec}); - } -} - -template -void threeLabelSpec(const A &s, std::list &ops, - const parser::Statement &ec, AnalysisData &ad) { - auto errLab{GetErr(s)}; - auto eorLab{GetEor(s)}; - auto endLab{GetEnd(s)}; - if (errLab || eorLab || endLab) { - std::optional errRef; - if (errLab) { - errRef = FetchLabel(ad, errLab).get(); - } - std::optional eorRef; - if (eorLab) { - eorRef = FetchLabel(ad, eorLab).get(); - } - std::optional endRef; - if (endLab) { - endRef = FetchLabel(ad, endLab).get(); - } - auto next{BuildNewLabel(ad)}; - ops.emplace_back(SwitchIOOp{s, next, ec.source, errRef, eorRef, endRef}); - ops.emplace_back(next); - } else { - ops.emplace_back(ActionOp{ec}); - } -} - -template -std::vector toLabelMention(AnalysisData &ad, const A &labels) { - std::vector result; - for (auto label : labels) { - result.emplace_back(FetchLabel(ad, label).get()); - } - CHECK(result.size() == labels.size()); - return result; -} - -template -std::vector toLabelMention( - const LabelOp &next, AnalysisData &ad, const A &labels) { - std::vector result; - result.emplace_back(next); - auto refs{toLabelMention(ad, labels)}; - result.insert(result.end(), refs.begin(), refs.end()); - CHECK(result.size() == labels.size() + 1); - return result; -} - -static bool hasAltReturns(const parser::CallStmt &callStmt) { - const auto &args{std::get>(callStmt.v.t)}; - for (const auto &arg : args) { - const auto &actual{std::get(arg.t)}; - if (std::holds_alternative(actual.u)) { - return true; - } - } - return false; -} - -static std::list getAltReturnLabels(const parser::Call &call) { - std::list result; - const auto &args{std::get>(call.t)}; - for (const auto &arg : args) { - const auto &actual{std::get(arg.t)}; - if (const auto *p{std::get_if(&actual.u)}) { - result.push_back(p->v); - } - } - return result; -} - -static LabelMention NearestEnclosingDoConstruct(AnalysisData &ad) { - for (auto iterator{ad.constructContextStack.rbegin()}, - endIterator{ad.constructContextStack.rend()}; - iterator != endIterator; ++iterator) { - auto labelReference{std::get<2>(*iterator)}; - if (labelReference != UnspecifiedLabel) { - return labelReference; - } - } - assert(false && "CYCLE|EXIT not in loop"); - return UnspecifiedLabel; -} - -template std::string GetSource(const A *s) { - return s->source.ToString(); -} - -template std::string GetSource(const B *s) { - return GetSource(&std::get>(s->t)); -} - -void Op::Build(std::list &ops, - const parser::Statement &ec, AnalysisData &ad) { - std::visit( - common::visitors{ - [&](const auto &) { ops.emplace_back(ActionOp{ec}); }, - [&](const common::Indirection &s) { - if (hasAltReturns(s.value())) { - auto next{BuildNewLabel(ad)}; - auto alts{getAltReturnLabels(s.value().v)}; - auto labels{toLabelMention(next, ad, alts)}; - ops.emplace_back( - SwitchOp{s.value(), std::move(labels), ec.source}); - ops.emplace_back(next); - } else { - ops.emplace_back(ActionOp{ec}); - } - }, - [&](const common::Indirection &s) { - AddAssign(ad, std::get(s.value().t).symbol, - std::get(s.value().t)); - ops.emplace_back(ActionOp{ec}); - }, - [&](const common::Indirection &s) { - ops.emplace_back(GotoOp{s.value(), - s.value().v ? std::get<2>(FindStack(ad.constructContextStack, - &s.value().v.value())) - : NearestEnclosingDoConstruct(ad), - ec.source}); - }, - [&](const common::Indirection &s) { - ops.emplace_back(GotoOp{s.value(), - s.value().v ? std::get<1>(FindStack(ad.constructContextStack, - &s.value().v.value())) - : NearestEnclosingDoConstruct(ad), - ec.source}); - }, - [&](const common::Indirection &s) { - ops.emplace_back(GotoOp{ - s.value(), FetchLabel(ad, s.value().v).get(), ec.source}); - }, - [&](const parser::FailImageStmt &s) { - ops.emplace_back(ReturnOp{s, ec.source}); - }, - [&](const common::Indirection &s) { - ops.emplace_back(ReturnOp{s.value(), ec.source}); - }, - [&](const common::Indirection &s) { - ops.emplace_back(ActionOp{ec}); - ops.emplace_back(ReturnOp{s.value(), ec.source}); - }, - [&](const common::Indirection &s) { - threeLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - threeLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - threeLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - errLabelSpec(s.value(), ops, ec, ad); - }, - [&](const common::Indirection &s) { - auto next{BuildNewLabel(ad)}; - auto labels{toLabelMention( - next, ad, std::get>(s.value().t))}; - ops.emplace_back(SwitchOp{s.value(), std::move(labels), ec.source}); - ops.emplace_back(next); - }, - [&](const common::Indirection &s) { - ops.emplace_back(SwitchOp{s.value(), - toLabelMention(ad, - std::list{std::get<1>(s.value().t), - std::get<2>(s.value().t), std::get<3>(s.value().t)}), - ec.source}); - }, - [&](const common::Indirection &s) { - ops.emplace_back( - IndirectGotoOp{std::get(s.value().t).symbol, - toLabelMention( - ad, std::get>(s.value().t))}); - }, - [&](const common::Indirection &s) { - auto then{BuildNewLabel(ad)}; - auto endif{BuildNewLabel(ad)}; - ops.emplace_back(ConditionalGotoOp{s.value(), then, endif}); - ops.emplace_back(then); - ops.emplace_back(ActionOp{ec}); - ops.emplace_back(endif); - }, - }, - ec.statement.u); -} - -template struct ElementMap; -template<> struct ElementMap { - using type = parser::CaseConstruct::Case; -}; -template<> struct ElementMap { - using type = parser::SelectRankConstruct::RankCase; -}; -template<> struct ElementMap { - using type = parser::SelectTypeConstruct::TypeCase; -}; - -struct ControlFlowAnalyzer { - explicit ControlFlowAnalyzer(std::list &ops, AnalysisData &ad) - : linearOps{ops}, ad{ad} {} - - LabelOp buildNewLabel() { return BuildNewLabel(ad); } - - Op findLabel(const parser::Label &lab) { - auto iter{ad.labelMap.find(lab)}; - if (iter == ad.labelMap.end()) { - LabelOp ll{ad.labelBuilder}; - ad.labelMap.insert({lab, ll}); - return {ll}; - } - return {iter->second}; - } - - template constexpr bool Pre(const A &) { return true; } - template constexpr void Post(const A &) {} - - template bool Pre(const parser::Statement &stmt) { - if (stmt.label) { - linearOps.emplace_back(findLabel(*stmt.label)); - } - if constexpr (std::is_same_v) { - Op::Build(linearOps, stmt, ad); - } - return true; - } - template - void appendIfLabeled(const parser::Statement &stmt, std::list &ops) { - if (stmt.label) { - ops.emplace_back(findLabel(*stmt.label)); - } - } - - // named constructs - template bool linearConstruct(const A &construct) { - std::list ops; - LabelOp label{buildNewLabel()}; - const parser::Name *name{getName(construct)}; - ad.constructContextStack.emplace_back( - name, GetLabelMention(label), UnspecifiedLabel); - appendIfLabeled(std::get<0>(construct.t), ops); - ops.emplace_back(BeginOp{construct}); - ControlFlowAnalyzer cfa{ops, ad}; - Walk(std::get(construct.t), cfa); - ops.emplace_back(label); - appendIfLabeled(std::get<2>(construct.t), ops); - ops.emplace_back(EndOp{construct}); - linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); - return false; - } - - bool Pre(const parser::AssociateConstruct &c) { return linearConstruct(c); } - bool Pre(const parser::ChangeTeamConstruct &c) { return linearConstruct(c); } - bool Pre(const parser::CriticalConstruct &c) { return linearConstruct(c); } - - bool Pre(const parser::BlockConstruct &construct) { - std::list ops; - LabelOp label{buildNewLabel()}; - const auto &optName{ - std::get>(construct.t) - .statement.v}; - const parser::Name *name{optName ? &*optName : nullptr}; - ad.constructContextStack.emplace_back( - name, GetLabelMention(label), UnspecifiedLabel); - appendIfLabeled( - std::get>(construct.t), ops); - ops.emplace_back(BeginOp{construct}); - ControlFlowAnalyzer cfa{ops, ad}; - Walk(std::get(construct.t), cfa); - appendIfLabeled( - std::get>(construct.t), ops); - ops.emplace_back(EndOp{construct}); - ops.emplace_back(label); - linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); - return false; - } - - /// `DO` constructs can be lowered to `fir.loop` if they meet some - /// constraints, otherwise they are lowered to a CFG. - bool Pre(const parser::DoConstruct &construct) { - std::list ops; - LabelOp backedgeLab{buildNewLabel()}; - LabelOp incrementLab{buildNewLabel()}; - LabelOp entryLab{buildNewLabel()}; - LabelOp exitLab{buildNewLabel()}; - const parser::Name *name{getName(construct)}; - LabelMention exitOpRef{GetLabelMention(exitLab)}; - ad.constructContextStack.emplace_back( - name, exitOpRef, GetLabelMention(incrementLab)); - appendIfLabeled( - std::get>(construct.t), ops); - ops.emplace_back(BeginOp{construct}); - ops.emplace_back(GotoOp{GetLabelMention(backedgeLab)}); - ops.emplace_back(incrementLab); - ops.emplace_back(DoIncrementOp{construct}); - ops.emplace_back(backedgeLab); - ops.emplace_back(DoCompareOp{construct}); - ops.emplace_back(ConditionalGotoOp{ - std::get>(construct.t), - GetLabelMention(entryLab), exitOpRef}); - ops.push_back(entryLab); - ControlFlowAnalyzer cfa{ops, ad}; - Walk(std::get(construct.t), cfa); - appendIfLabeled( - std::get>(construct.t), ops); - ops.emplace_back(GotoOp{GetLabelMention(incrementLab)}); - ops.emplace_back(EndOp{construct}); - ops.emplace_back(exitLab); - linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); - return false; - } - - /// `IF` constructs can be lowered to `fir.where` if they meet some - /// constraints, otherwise they are lowered to a CFG. - bool Pre(const parser::IfConstruct &construct) { - std::list ops; - LabelOp thenLab{buildNewLabel()}; - LabelOp elseLab{buildNewLabel()}; - LabelOp exitLab{buildNewLabel()}; - const parser::Name *name{getName(construct)}; - ad.constructContextStack.emplace_back( - name, GetLabelMention(exitLab), UnspecifiedLabel); - appendIfLabeled( - std::get>(construct.t), ops); - ops.emplace_back(BeginOp{construct}); - ops.emplace_back(ConditionalGotoOp{ - std::get>(construct.t), - GetLabelMention(thenLab), GetLabelMention(elseLab)}); - ops.emplace_back(thenLab); - ControlFlowAnalyzer cfa{ops, ad}; - Walk(std::get(construct.t), cfa); - LabelMention exitOpRef{GetLabelMention(exitLab)}; - ops.emplace_back(GotoOp{exitOpRef}); - for (const auto &elseIfBlock : - std::get>(construct.t)) { - appendIfLabeled( - std::get>(elseIfBlock.t), ops); - ops.emplace_back(elseLab); - LabelOp newThenLab{buildNewLabel()}; - LabelOp newElseLab{buildNewLabel()}; - ops.emplace_back(ConditionalGotoOp{ - std::get>(elseIfBlock.t), - GetLabelMention(newThenLab), GetLabelMention(newElseLab)}); - ops.emplace_back(newThenLab); - Walk(std::get(elseIfBlock.t), cfa); - ops.emplace_back(GotoOp{exitOpRef}); - elseLab = newElseLab; - } - ops.emplace_back(elseLab); - if (const auto &optElseBlock{ - std::get>( - construct.t)}) { - appendIfLabeled( - std::get>(optElseBlock->t), ops); - Walk(std::get(optElseBlock->t), cfa); - } - ops.emplace_back(GotoOp{exitOpRef}); - ops.emplace_back(exitLab); - appendIfLabeled( - std::get>(construct.t), ops); - ops.emplace_back(EndOp{construct}); - linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); - return false; - } - - template bool Multiway(const A &construct) { - using B = typename ElementMap::type; - std::list ops; - LabelOp exitLab{buildNewLabel()}; - const parser::Name *name{getName(construct)}; - ad.constructContextStack.emplace_back( - name, GetLabelMention(exitLab), UnspecifiedLabel); - appendIfLabeled(std::get<0>(construct.t), ops); - ops.emplace_back(BeginOp{construct}); - const auto N{std::get>(construct.t).size()}; - LabelMention exitOpRef{GetLabelMention(exitLab)}; - if (N > 0) { - typename std::list::size_type i; - std::vector toLabels; - for (i = 0; i != N; ++i) { - toLabels.emplace_back(buildNewLabel()); - } - std::vector targets; - for (i = 0; i != N; ++i) { - targets.emplace_back(GetLabelMention(toLabels[i])); - } - ops.emplace_back( - SwitchOp{construct, targets, std::get<0>(construct.t).source}); - ControlFlowAnalyzer cfa{ops, ad}; - i = 0; - for (const auto &caseBlock : std::get>(construct.t)) { - ops.emplace_back(toLabels[i++]); - appendIfLabeled(std::get<0>(caseBlock.t), ops); - Walk(std::get(caseBlock.t), cfa); - ops.emplace_back(GotoOp{exitOpRef}); - } - } - ops.emplace_back(exitLab); - appendIfLabeled(std::get<2>(construct.t), ops); - ops.emplace_back(EndOp{construct}); - linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); - return false; - } - - bool Pre(const parser::CaseConstruct &c) { return Multiway(c); } - bool Pre(const parser::SelectRankConstruct &c) { return Multiway(c); } - bool Pre(const parser::SelectTypeConstruct &c) { return Multiway(c); } - - bool Pre(const parser::WhereConstruct &c) { - std::list ops; - LabelOp label{buildNewLabel()}; - const parser::Name *name{getName(c)}; - ad.constructContextStack.emplace_back( - name, GetLabelMention(label), UnspecifiedLabel); - appendIfLabeled( - std::get>(c.t), ops); - ops.emplace_back(BeginOp{c}); - ControlFlowAnalyzer cfa{ops, ad}; - Walk(std::get>(c.t), cfa); - Walk( - std::get>(c.t), cfa); - Walk(std::get>(c.t), cfa); - ops.emplace_back(label); - appendIfLabeled( - std::get>(c.t), ops); - ops.emplace_back(EndOp{c}); - linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); - return false; - } - - bool Pre(const parser::ForallConstruct &construct) { - std::list ops; - LabelOp label{buildNewLabel()}; - const parser::Name *name{getName(construct)}; - ad.constructContextStack.emplace_back( - name, GetLabelMention(label), UnspecifiedLabel); - appendIfLabeled( - std::get>(construct.t), - ops); - ops.emplace_back(BeginOp{construct}); - ControlFlowAnalyzer cfa{ops, ad}; - Walk(std::get>(construct.t), cfa); - ops.emplace_back(label); - appendIfLabeled( - std::get>(construct.t), ops); - ops.emplace_back(EndOp{construct}); - linearOps.splice(linearOps.end(), ops); - ad.constructContextStack.pop_back(); - return false; - } - - template const parser::Name *getName(const A &a) { - const auto &optName{std::get<0>(std::get<0>(a.t).statement.t)}; - return optName ? &*optName : nullptr; - } - - LabelMention GetLabelMention(const LabelOp &label) { - label.setReferenced(); - return label; - } - - LabelMention GetLabelMention(const parser::Label &label) { - return FetchLabel(ad, label); - } - - std::list &linearOps; - AnalysisData &ad; -}; - -} // namespace flat - -void CreateFlatIR(std::list &ops, AnalysisData &ad) { - flat::ControlFlowAnalyzer linearize{ops, ad}; - std::visit( - [&](const auto *ptree) { Walk(*ptree, linearize); }, ad.parseTreeRoot); -} - -} // namespace burnside diff --git a/lib/burnside/flattened.h b/lib/burnside/flattened.h deleted file mode 100644 index 36c32c3c98d0..000000000000 --- a/lib/burnside/flattened.h +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef FORTRAN_BURNSIDE_FLATTENED_H_ -#define FORTRAN_BURNSIDE_FLATTENED_H_ - -#include "mixin.h" -#include "../parser/parse-tree.h" -#include "llvm/ADT/BitVector.h" -#include -#include -#include -#include -#include - -namespace Fortran::burnside { - -struct AnalysisData; - -namespace flat { - -/// This is a flattened, linearized representation of the parse -/// tree. It captures the executable specification of the input -/// program. The flattened IR can be used to construct FIR. -/// -/// [Coding style](https://llvm.org/docs/CodingStandards.html) - -using LabelMention = unsigned; -constexpr LabelMention UnspecifiedLabel{UINT_MAX}; - -using Location = parser::CharBlock; -struct LabelBuilder; - -// target for a control-flow edge -struct LabelOp { - explicit LabelOp(LabelBuilder &builder); - LabelOp(const LabelOp &that); - LabelOp &operator=(const LabelOp &that); - void setReferenced() const; - bool isReferenced() const; - LabelMention get() const { return label; } - operator LabelMention() const { return get(); } - -private: - LabelBuilder &builder; - LabelMention label; -}; - -struct ArtificialJump {}; - -// a source of an absolute control flow edge -struct GotoOp - : public SumTypeCopyMixin { - template - explicit GotoOp(const A &stmt, LabelMention dest, const Location &source) - : SumTypeCopyMixin{&stmt}, target{dest}, source{source} {} - explicit GotoOp(LabelMention dest) - : SumTypeCopyMixin{ArtificialJump{}}, target{dest} {} - - LabelMention target; - Location source; -}; - -// control exits the procedure -struct ReturnOp : public SumTypeCopyMixin { - template - explicit ReturnOp(const A &stmt, const Location &source) - : SumTypeCopyMixin{&stmt}, source{source} {} - - Location source; -}; - -// two-way branch based on a condition -struct ConditionalGotoOp - : public SumTypeCopyMixin *, - const parser::Statement *, const parser::IfStmt *, - const parser::Statement *> { - template - explicit ConditionalGotoOp(const A &cond, LabelMention tb, LabelMention fb) - : SumTypeCopyMixin{&cond}, trueLabel{tb}, falseLabel{fb} {} - - LabelMention trueLabel; - LabelMention falseLabel; -}; - -// multi-way branch based on a target-value of a variable -struct IndirectGotoOp { - explicit IndirectGotoOp( - const semantics::Symbol *symbol, std::vector &&labelRefs) - : labelRefs{labelRefs}, symbol{symbol} {} - - std::vector labelRefs; - const semantics::Symbol *symbol; -}; - -// intrinsic IO operations can return with an implied multi-way branch -struct SwitchIOOp - : public SumTypeCopyMixin { - template - explicit SwitchIOOp(const A &io, LabelMention next, const Location &source, - std::optional errLab, - std::optional eorLab = std::nullopt, - std::optional endLab = std::nullopt) - : SumTypeCopyMixin{&io}, next{next}, source{source}, errLabel{errLab}, - eorLabel{eorLab}, endLabel{endLab} {} - LabelMention next; - Location source; - std::optional errLabel; - std::optional eorLabel; - std::optional endLabel; -}; - -// multi-way branch based on conditions -struct SwitchOp - : public SumTypeCopyMixin { - template - explicit SwitchOp(const A &sw, const std::vector &refs, - const Location &source) - : SumTypeCopyMixin{&sw}, refs{refs}, source{source} {} - - const std::vector refs; - Location source; -}; - -// a compute step -struct ActionOp { - explicit ActionOp(const parser::Statement &stmt) - : v{&stmt} {} - - const parser::Statement *v; -}; - -#define CONSTRUCT_TYPES \ - const parser::AssociateConstruct *, const parser::BlockConstruct *, \ - const parser::CaseConstruct *, const parser::ChangeTeamConstruct *, \ - const parser::CriticalConstruct *, const parser::DoConstruct *, \ - const parser::IfConstruct *, const parser::SelectRankConstruct *, \ - const parser::SelectTypeConstruct *, const parser::WhereConstruct *, \ - const parser::ForallConstruct *, const parser::CompilerDirective *, \ - const parser::OpenMPConstruct *, const parser::OmpEndLoopDirective * - -// entry into a Fortran construct -struct BeginOp : public SumTypeCopyMixin { - SUM_TYPE_COPY_MIXIN(BeginOp) - - template explicit BeginOp(const A &c) : SumTypeCopyMixin{&c} {} -}; - -// exit from a Fortran construct -struct EndOp : public SumTypeCopyMixin { - SUM_TYPE_COPY_MIXIN(EndOp) - - template explicit EndOp(const A &c) : SumTypeCopyMixin{&c} {} -}; - -struct DoIncrementOp { - explicit DoIncrementOp(const parser::DoConstruct &stmt) : v{&stmt} {} - - const parser::DoConstruct *v; -}; - -struct DoCompareOp { - DoCompareOp(const parser::DoConstruct &stmt) : v{&stmt} {} - - const parser::DoConstruct *v; -}; - -// the flat structure is a list of Ops, where an Op is any of ... -struct Op : public SumTypeMixin { - template Op(const A &thing) : SumTypeMixin{thing} {} - - static void Build(std::list &ops, - const parser::Statement &ec, AnalysisData &ad); -}; - -// helper to build unique labels -struct LabelBuilder { - LabelBuilder(); - LabelMention getNext(); - void setReferenced(LabelMention label); - bool isReferenced(LabelMention label) const; - llvm::BitVector referenced; - unsigned counter; -}; - -LabelOp FetchLabel(AnalysisData &ad, const parser::Label &label); - -std::vector GetAssign( - AnalysisData &ad, const semantics::Symbol *symbol); - -} // namespace flat - -// Collection of data maintained internally by the flattening algorithm -struct AnalysisData { - template - AnalysisData(const Node &node) : parseTreeRoot{&node} {} - - std::map labelMap; - std::vector< - std::tuple> - constructContextStack; - flat::LabelBuilder labelBuilder; - std::map> assignMap; - const std::variant - parseTreeRoot; -}; - -void CreateFlatIR(std::list &, AnalysisData &); - -} // namespace burnside - -#endif // FORTRAN_BURNSIDE_FLATTENED_H_ From ef6f6899d437089ace0158d942d3af41b0ae0b66 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 5 Dec 2019 13:41:47 -0800 Subject: [PATCH 054/123] WIP: loops and conditionals --- lib/burnside/ast-builder.cc | 2 + lib/burnside/ast-builder.h | 1 + lib/burnside/bridge.cc | 143 ++++++++++++++++++++++++++++-------- 3 files changed, 116 insertions(+), 30 deletions(-) diff --git a/lib/burnside/ast-builder.cc b/lib/burnside/ast-builder.cc index 7562e4ff4d82..db06e7457d32 100644 --- a/lib/burnside/ast-builder.cc +++ b/lib/burnside/ast-builder.cc @@ -596,6 +596,8 @@ void annotateEvalListCFG( } } +/// Annotate the AST with CFG source decorations (see CFGAnnotation) and mark +/// potential branch targets inline void annotateFuncCFG(AST::FunctionLikeUnit &flu) { annotateEvalListCFG(flu.evals, nullptr); } diff --git a/lib/burnside/ast-builder.h b/lib/burnside/ast-builder.h index 0b64868598e5..c639d6044cd1 100644 --- a/lib/burnside/ast-builder.h +++ b/lib/burnside/ast-builder.h @@ -37,6 +37,7 @@ enum class CFGAnnotation { IoSwitch, Switch, Iterative, + FirStructuredOp, Return, Terminate }; diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index babe24387614..7f87b00a84d4 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -52,6 +52,10 @@ using namespace Fortran::burnside; namespace { +using SelectCaseConstruct = Pa::CaseConstruct; +using SelectRankConstruct = Pa::SelectRankConstruct; +using SelectTypeConstruct = Pa::SelectTypeConstruct; + using CFGSinkListType = L::SmallVector; using CFGMapType = L::DenseMap; @@ -111,8 +115,61 @@ class CfgBuilder { } } + void deannotate(std::list &evals) { + for (auto &e : evals) { + e.cfg = AST::CFGAnnotation::None; + if (e.subs) { + deannotate(*e.subs); + } + } + } + + bool structuredCheck(std::list &evals) { + for (auto &e : evals) { + if (std::holds_alternative(e.u)) { + return structuredCheck(*e.subs); + } + if (std::holds_alternative(e.u)) { + return structuredCheck(*e.subs); + } + if (e.subs) { + return false; + } + switch (e.cfg) { + case AST::CFGAnnotation::None: break; + case AST::CFGAnnotation::Goto: break; + case AST::CFGAnnotation::Iterative: break; + case AST::CFGAnnotation::FirStructuredOp: break; + case AST::CFGAnnotation::CondGoto: + if (!std::holds_alternative(e.u)) { + return false; + } + break; + case AST::CFGAnnotation::IndGoto: return false; + case AST::CFGAnnotation::IoSwitch: return false; + case AST::CFGAnnotation::Switch: return false; + case AST::CFGAnnotation::Return: return false; + case AST::CFGAnnotation::Terminate: return false; + } + } + return true; + } + void wrapIterationSpaces(std::list &evals) { for (auto &e : evals) { + if (std::holds_alternative(e.u)) + if (structuredCheck(*e.subs)) { + deannotate(*e.subs); + e.cfg = AST::CFGAnnotation::FirStructuredOp; + continue; + } + if (std::holds_alternative(e.u)) + if (structuredCheck(*e.subs)) { + deannotate(*e.subs); + e.cfg = AST::CFGAnnotation::FirStructuredOp; + continue; + } + // FIXME: ForallConstruct? WhereConstruct? if (e.subs) { wrapIterationSpaces(*e.subs); } @@ -290,6 +347,7 @@ class CfgBuilder { }, e.u); break; + case AST::CFGAnnotation::FirStructuredOp: continue; case AST::CFGAnnotation::Return: // do nothing - exits the function break; @@ -303,6 +361,13 @@ class CfgBuilder { } } + void setActualTargets(std::list &evals) { + for (auto &lst1 : cfgEdgeSetPool) + for (auto *e : lst1) { + e->isTarget = true; + } + } + CFGMapType &cfgMap; std::list &cfgEdgeSetPool; @@ -318,6 +383,7 @@ class CfgBuilder { cacheAssigns(func.evals); wrapIterationSpaces(func.evals); reachabilityAnalysis(func.evals); + setActualTargets(func.evals); } }; @@ -590,6 +656,11 @@ class FirConverter { // FIXME } + // Structured control op (fir.loop, fir.where) + void genFIREvalStructuredOp(AST::Evaluation &eval) { + // FIXME + } + // Return from subprogram control-flow semantics void genFIREvalReturn(AST::Evaluation &eval) { // Handled case-by-case @@ -639,7 +710,7 @@ class FirConverter { void genFIR(const Pa::AssociateConstruct &) { TODO(); } void genFIR(const Pa::BlockConstruct &) { TODO(); } - void genFIR(const Pa::CaseConstruct &) { TODO(); } + void genFIR(const SelectCaseConstruct &) { TODO(); } void genFIR(const Pa::ChangeTeamConstruct &) { TODO(); } void genFIR(const Pa::CriticalConstruct &) { TODO(); } void genFIR(const Pa::DoConstruct &d) { @@ -656,8 +727,8 @@ class FirConverter { TODO(); } void genFIR(const Pa::IfConstruct &cst) { TODO(); } - void genFIR(const Pa::SelectRankConstruct &) { TODO(); } - void genFIR(const Pa::SelectTypeConstruct &) { TODO(); } + void genFIR(const SelectRankConstruct &) { TODO(); } + void genFIR(const SelectTypeConstruct &) { TODO(); } void genFIR(const Pa::WhereConstruct &) { TODO(); } /// Lower FORALL construct (See 10.2.4) @@ -709,19 +780,22 @@ class FirConverter { void genFIR(const parser::EndChangeTeamStmt &) { TODO(); } void genFIR(const parser::CriticalStmt &) { TODO(); } void genFIR(const parser::EndCriticalStmt &) { TODO(); } - void genFIR(const parser::NonLabelDoStmt &) { TODO(); } - void genFIR(const parser::EndDoStmt &) { TODO(); } - // If-Then-Else is handled by genFIREvalCondGoto() - void genFIR(const parser::IfThenStmt &) {} /* do nothing */ - void genFIR(const parser::ElseIfStmt &) {} /* do nothing */ - void genFIR(const parser::ElseStmt &) {} /* do nothing */ - void genFIR(const parser::EndIfStmt &) {} /* do nothing */ + // Do loop is handled by EvalIterative(), EvalStructuredOp() + void genFIR(const parser::NonLabelDoStmt &) {} // do nothing + void genFIR(const parser::EndDoStmt &) {} // do nothing + + // If-Then-Else is handled by EvalCondGoto(), EvalStructuredOp() + void genFIR(const parser::IfThenStmt &) {} // do nothing + void genFIR(const parser::ElseIfStmt &) {} // do nothing + void genFIR(const parser::ElseStmt &) {} // do nothing + void genFIR(const parser::EndIfStmt &) {} // do nothing void genFIR(const parser::SelectRankStmt &) { TODO(); } void genFIR(const parser::SelectRankCaseStmt &) { TODO(); } void genFIR(const parser::SelectTypeStmt &) { TODO(); } void genFIR(const parser::TypeGuardStmt &) { TODO(); } + void genFIR(const parser::WhereConstructStmt &) { TODO(); } void genFIR(const parser::MaskedElsewhereStmt &) { TODO(); } void genFIR(const parser::ElsewhereStmt &) { TODO(); } @@ -770,19 +844,31 @@ class FirConverter { genPrintStatement(*builder, toLocation(), args); } - void genFIR(const Pa::ReadStmt &) { TODO(); } + void genFIR(const Pa::ReadStmt &) { + // call some IO runtime routine(s) + TODO(); + } void genFIR(const Pa::RewindStmt &) { TODO(); } void genFIR(const Pa::SyncAllStmt &) { TODO(); } void genFIR(const Pa::SyncImagesStmt &) { TODO(); } void genFIR(const Pa::SyncMemoryStmt &) { TODO(); } void genFIR(const Pa::SyncTeamStmt &) { TODO(); } - void genFIR(const Pa::UnlockStmt &) { TODO(); } + void genFIR(const Pa::UnlockStmt &) { + // call some runtime routine + TODO(); + } - void genFIR(const Pa::WriteStmt &) { TODO(); } + void genFIR(const Pa::WriteStmt &) { + // call some IO runtime routine(s) + TODO(); + } void genFIR(const Pa::AssignStmt &) { TODO(); } void genFIR(const Pa::FormatStmt &) { TODO(); } void genFIR(const Pa::EntryStmt &) { TODO(); } - void genFIR(const Pa::PauseStmt &) { TODO(); } + void genFIR(const Pa::PauseStmt &) { + // call some runtime routine + TODO(); + } void genFIR(const Pa::DataStmt &) { TODO(); } void genFIR(const Pa::NamelistStmt &) { TODO(); } @@ -803,31 +889,25 @@ class FirConverter { builder->create(toLocation(), callee, operands); } - // gen expression, if any + // gen expression, if any; share code with END of procedure void genFIR(const Pa::ReturnStmt &stmt) { if (inMainProgram(currentEvaluation)) { builder->create(toLocation()); - return; - } - if (auto *stmt = inSubroutine(currentEvaluation)) { + } else if (auto *stmt = inSubroutine(currentEvaluation)) { genFIRProcedureExit(stmt); - return; - } - if (auto *stmt = inFunction(currentEvaluation)) { + } else if (auto *stmt = inFunction(currentEvaluation)) { genFIRFunctionReturn(stmt); - return; - } - if (auto *stmt = inMPSubp(currentEvaluation)) { + } else if (auto *stmt = inMPSubp(currentEvaluation)) { genFIRProcedureExit(stmt); - return; + } else { + assert(false && "unknown subprogram type"); } - assert(false && "unknown subprogram type"); } - // stubs for generic goto statements; see genFIREvalGoto - void genFIR(const Pa::CycleStmt &) { assert(false && "invalid"); } - void genFIR(const Pa::ExitStmt &) { assert(false && "invalid"); } - void genFIR(const Pa::GotoStmt &) { assert(false && "invalid"); } + // stubs for generic goto statements; see genFIREvalGoto() + void genFIR(const Pa::CycleStmt &) {} // do nothing + void genFIR(const Pa::ExitStmt &) {} // do nothing + void genFIR(const Pa::GotoStmt &) {} // do nothing void genFIR(AST::Evaluation &eval) { currentEvaluation = &eval; @@ -851,6 +931,9 @@ class FirConverter { case AST::CFGAnnotation::IoSwitch: genFIREvalIoSwitch(eval); break; case AST::CFGAnnotation::Switch: genFIREvalSwitch(eval); break; case AST::CFGAnnotation::Iterative: genFIREvalIterative(eval); break; + case AST::CFGAnnotation::FirStructuredOp: + genFIREvalStructuredOp(eval); + break; case AST::CFGAnnotation::Return: genFIREvalReturn(eval); break; case AST::CFGAnnotation::Terminate: genFIREvalTerminate(eval); break; } From 15f46487ae7a883796d233b39ec7f2cbee23f589 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 5 Dec 2019 16:14:03 -0800 Subject: [PATCH 055/123] changes --- lib/burnside/bridge.cc | 185 +++++++++++++++++++++++++++++++++---- lib/burnside/intrinsics.cc | 1 + 2 files changed, 168 insertions(+), 18 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 7f87b00a84d4..c89f14ec6a22 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -124,9 +124,25 @@ class CfgBuilder { } } + bool structuredLoop(const std::optional &optLoopCtrl) { + if (optLoopCtrl.has_value()) + return std::visit( + Co::visitors{ + [](const Pa::LoopControl::Bounds &) { return true; }, + [](const Pa::ScalarLogicalExpr &) { return false; }, + [](const Pa::LoopControl::Concurrent &) { return true; }, + }, + optLoopCtrl->u); + return false; + } + bool structuredCheck(std::list &evals) { for (auto &e : evals) { - if (std::holds_alternative(e.u)) { + if (auto **s = std::get_if(&e.u)) { + if (!structuredLoop(std::get>( + std::get>((*s)->t) + .statement.t))) + return false; return structuredCheck(*e.subs); } if (std::holds_alternative(e.u)) { @@ -656,9 +672,95 @@ class FirConverter { // FIXME } + void switchInsertionPointToWhere(fir::WhereOp &where) { + // FIXME + } + void switchInsertionPointToOtherwise(fir::WhereOp &where) { + // FIXME + } + template + void handleCondition(fir::WhereOp &where, const A *stmt) { + auto *cond{createLogicalExprAsI1( + toLocation(), Se::GetExpr(std::get(stmt->t)))}; + where = builder->create(toLocation(), cond, true); + switchInsertionPointToWhere(where); + } + // Structured control op (fir.loop, fir.where) void genFIREvalStructuredOp(AST::Evaluation &eval) { - // FIXME + // process the list of Evaluations + assert(eval.subs); + auto *insPt = builder->getInsertionBlock(); + + if (std::holds_alternative(eval.u)) { + // Construct fir.loop + fir::LoopOp doLoop; + for (auto &e : *eval.subs) { + if (auto **s = std::get_if(&e.u)) { + // do bounds, fir.loop op + std::visit( + Co::visitors{ + [&](const Pa::LoopControl::Bounds &x) { + auto *lo = + createFIRExpr(toLocation(), Se::GetExpr(x.lower)); + auto *hi = + createFIRExpr(toLocation(), Se::GetExpr(x.upper)); + L::SmallVector step; + if (x.step.has_value()) { + step.emplace_back( + createFIRExpr(toLocation(), Se::GetExpr(*x.step))); + } + doLoop = builder->create( + toLocation(), lo, hi, step); + }, + [](const Pa::ScalarLogicalExpr &) { + assert(false && "loop lacks iteration space"); + }, + [&](const Pa::LoopControl::Concurrent &x) { + // FIXME: can project a multi-dimensional space + doLoop = builder->create(toLocation(), + (M::Value *)nullptr, (M::Value *)nullptr, + L::ArrayRef{}); + }, + }, + std::get>((*s)->t)->u); + } else if (std::holds_alternative(e.u)) { + // close fir.loop op + builder->clearInsertionPoint(); + } else { + genFIR(e); + } + } + } else if (std::holds_alternative(eval.u)) { + // Construct fir.where + fir::WhereOp where; + bool hasElse = false; + for (auto &e : *eval.subs) { + if (auto **s = std::get_if(&e.u)) { + // fir.where op + handleCondition(where, *s); + } else if (auto **s = std::get_if(&e.u)) { + // otherwise block, then nested fir.where + switchInsertionPointToOtherwise(where); + handleCondition(where, *s); + } else if (std::holds_alternative(e.u)) { + // otherwise block + switchInsertionPointToOtherwise(where); + hasElse = true; + } else if (std::holds_alternative(e.u)) { + // close all open fir.where ops + if (!hasElse) { + switchInsertionPointToOtherwise(where); + } + builder->clearInsertionPoint(); + } else { + genFIR(e); + } + } + } else { + assert(false && "not yet implemented"); + } + builder->setInsertionPointToEnd(insPt); } // Return from subprogram control-flow semantics @@ -815,20 +917,47 @@ class FirConverter { builder->create( loc, createFIRExpr(loc, rhs), createFIRAddr(loc, lhs)); } - void genFIR(const Pa::BackspaceStmt &) { TODO(); } - void genFIR(const Pa::CloseStmt &) { TODO(); } - void genFIR(const Pa::ContinueStmt &) { TODO(); } + void genFIR(const Pa::BackspaceStmt &) { + // call some IO runtime routine(s) + TODO(); + } + void genFIR(const Pa::CloseStmt &) { + // call some IO runtime routine(s) + TODO(); + } + void genFIR(const Pa::ContinueStmt &) {} // do nothing void genFIR(const Pa::DeallocateStmt &) { TODO(); } - void genFIR(const Pa::EndfileStmt &) { TODO(); } - void genFIR(const Pa::EventPostStmt &) { TODO(); } - void genFIR(const Pa::EventWaitStmt &) { TODO(); } - void genFIR(const Pa::FlushStmt &) { TODO(); } + void genFIR(const Pa::EndfileStmt &) { + // call some IO runtime routine(s) + TODO(); + } + void genFIR(const Pa::EventPostStmt &) { + // call some runtime routine + TODO(); + } + void genFIR(const Pa::EventWaitStmt &) { + // call some runtime routine + TODO(); + } + void genFIR(const Pa::FlushStmt &) { + // call some IO runtime routine(s) + TODO(); + } void genFIR(const Pa::FormTeamStmt &) { TODO(); } - void genFIR(const Pa::InquireStmt &) { TODO(); } - void genFIR(const Pa::LockStmt &) { TODO(); } + void genFIR(const Pa::InquireStmt &) { + // call some IO runtime routine(s) + TODO(); + } + void genFIR(const Pa::LockStmt &) { + // call some runtime routine + TODO(); + } void genFIR(const Pa::NullifyStmt &) { TODO(); } - void genFIR(const Pa::OpenStmt &) { TODO(); } + void genFIR(const Pa::OpenStmt &) { + // call some IO runtime routine(s) + TODO(); + } void genFIR(const Pa::PointerAssignmentStmt &) { TODO(); } void genFIR(const Pa::PrintStmt &stmt) { @@ -848,11 +977,26 @@ class FirConverter { // call some IO runtime routine(s) TODO(); } - void genFIR(const Pa::RewindStmt &) { TODO(); } - void genFIR(const Pa::SyncAllStmt &) { TODO(); } - void genFIR(const Pa::SyncImagesStmt &) { TODO(); } - void genFIR(const Pa::SyncMemoryStmt &) { TODO(); } - void genFIR(const Pa::SyncTeamStmt &) { TODO(); } + void genFIR(const Pa::RewindStmt &) { + // call some IO runtime routine(s) + TODO(); + } + void genFIR(const Pa::SyncAllStmt &) { + // call some runtime routine + TODO(); + } + void genFIR(const Pa::SyncImagesStmt &) { + // call some runtime routine + TODO(); + } + void genFIR(const Pa::SyncMemoryStmt &) { + // call some runtime routine + TODO(); + } + void genFIR(const Pa::SyncTeamStmt &) { + // call some runtime routine + TODO(); + } void genFIR(const Pa::UnlockStmt &) { // call some runtime routine TODO(); @@ -913,11 +1057,16 @@ class FirConverter { currentEvaluation = &eval; std::visit(Co::visitors{ [&](const auto *p) { genFIR(*p); }, - [](const AST::CGJump &) { assert(false && "invalid"); }, + [](const AST::CGJump &) { /* do nothing */ }, }, eval.u); } + /// Lower an Evaluation + /// + /// If the Evaluation is annotated, we can attempt to lower it by the class of + /// annotation. Otherwise, attempt to lower the Evaluation on a case-by-case + /// basis. void lowerEval(AST::Evaluation &eval) { setCurrentPosition(eval.pos); if (eval.isControlTarget()) { diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index e5beb1432e4a..5b231903d6b2 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -438,6 +438,7 @@ mlir::Value *IntrinsicLibrary::Implementation::genRuntimeCall( // TODO: better error handling ? // - Try to have compile time check of runtime compltness ? } + return {}; // gets rid of warnings } // CONJG From 472de96b0f37543a010b7e423b1a27c127934245 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Fri, 6 Dec 2019 08:50:50 -0800 Subject: [PATCH 056/123] bug fix --- lib/burnside/bridge.cc | 90 +++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index c89f14ec6a22..172b99c504d0 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -33,7 +33,9 @@ #include "mlir/Target/LLVMIR.h" #undef TODO -#define TODO() assert(false && "not yet implemented") +#define TODO() \ + llvm::errs() << __FILE__ << ":" << __LINE__ << " not yet implemented\n"; \ + std::exit(1) #undef SOFT_TODO #define SOFT_TODO() \ @@ -153,19 +155,19 @@ class CfgBuilder { } switch (e.cfg) { case AST::CFGAnnotation::None: break; - case AST::CFGAnnotation::Goto: break; + case AST::CFGAnnotation::CondGoto: break; case AST::CFGAnnotation::Iterative: break; case AST::CFGAnnotation::FirStructuredOp: break; - case AST::CFGAnnotation::CondGoto: - if (!std::holds_alternative(e.u)) { - return false; - } - break; case AST::CFGAnnotation::IndGoto: return false; case AST::CFGAnnotation::IoSwitch: return false; case AST::CFGAnnotation::Switch: return false; case AST::CFGAnnotation::Return: return false; case AST::CFGAnnotation::Terminate: return false; + case AST::CFGAnnotation::Goto: + if (!std::holds_alternative(e.u)) { + return false; + } + break; } } return true; @@ -283,7 +285,10 @@ class CfgBuilder { evals.insert(iter, std::move(jumpEval)); addSourceToSink(&e, &cstr->subs->front()); }, - [](auto) { assert(false); }, + [&](const AST::CGJump &jump) { + addSourceToSink(&e, jump.target); + }, + [](auto) { assert(false && "unhandled GOTO case"); }, }, e.u); break; @@ -303,7 +308,7 @@ class CfgBuilder { }, [](const Pa::WhereConstructStmt *stmt) { TODO(); }, [](const Pa::MaskedElsewhereStmt *stmt) { TODO(); }, - [](auto) { assert(false); }, + [](auto) { assert(false && "unhandled CGOTO case"); }, }, e.u); break; @@ -320,24 +325,25 @@ class CfgBuilder { addSourceToSink(&e, l); } }, - [](auto) { assert(false); }, + [](auto) { assert(false && "unhandled IGOTO case"); }, }, e.u); break; case AST::CFGAnnotation::IoSwitch: - std::visit(Co::visitors{ - [](const Pa::BackspaceStmt *stmt) { TODO(); }, - [](const Pa::CloseStmt *stmt) { TODO(); }, - [](const Pa::EndfileStmt *stmt) { TODO(); }, - [](const Pa::FlushStmt *stmt) { TODO(); }, - [](const Pa::InquireStmt *stmt) { TODO(); }, - [](const Pa::OpenStmt *stmt) { TODO(); }, - [](const Pa::ReadStmt *stmt) { TODO(); }, - [](const Pa::RewindStmt *stmt) { TODO(); }, - [](const Pa::WaitStmt *stmt) { TODO(); }, - [](const Pa::WriteStmt *stmt) { TODO(); }, - [](auto) { assert(false); }, - }, + std::visit( + Co::visitors{ + [](const Pa::BackspaceStmt *stmt) { TODO(); }, + [](const Pa::CloseStmt *stmt) { TODO(); }, + [](const Pa::EndfileStmt *stmt) { TODO(); }, + [](const Pa::FlushStmt *stmt) { TODO(); }, + [](const Pa::InquireStmt *stmt) { TODO(); }, + [](const Pa::OpenStmt *stmt) { TODO(); }, + [](const Pa::ReadStmt *stmt) { TODO(); }, + [](const Pa::RewindStmt *stmt) { TODO(); }, + [](const Pa::WaitStmt *stmt) { TODO(); }, + [](const Pa::WriteStmt *stmt) { TODO(); }, + [](auto) { assert(false && "unhandled IO switch case"); }, + }, e.u); break; case AST::CFGAnnotation::Switch: @@ -348,7 +354,7 @@ class CfgBuilder { [](const Pa::SelectCaseStmt *stmt) { TODO(); }, [](const Pa::SelectRankStmt *stmt) { TODO(); }, [](const Pa::SelectTypeStmt *stmt) { TODO(); }, - [](auto) { assert(false); }, + [](auto) { assert(false && "unhandled switch case"); }, }, e.u); break; @@ -359,11 +365,13 @@ class CfgBuilder { [](const Pa::ForallStmt *stmt) { TODO(); }, [](const Pa::WhereConstruct *stmt) { TODO(); }, [](const Pa::ForallConstructStmt *stmt) { TODO(); }, - [](auto) { assert(false); }, + [](auto) { assert(false && "unhandled loop case"); }, }, e.u); break; - case AST::CFGAnnotation::FirStructuredOp: continue; + case AST::CFGAnnotation::FirStructuredOp: + // do not visit the subs + continue; case AST::CFGAnnotation::Return: // do nothing - exits the function break; @@ -417,16 +425,16 @@ class FirConverter { M::Value *createFIRAddr(M::Location loc, const Se::SomeExpr *expr) { return createSomeAddress( - loc, *builder, *expr, symbolMap, defaults, intrinsics); + loc, *builder, *expr, localSymbols, defaults, intrinsics); } M::Value *createFIRExpr(M::Location loc, const Se::SomeExpr *expr) { return createSomeExpression( - loc, *builder, *expr, symbolMap, defaults, intrinsics); + loc, *builder, *expr, localSymbols, defaults, intrinsics); } M::Value *createLogicalExprAsI1(M::Location loc, const Se::SomeExpr *expr) { return createI1LogicalExpression( - loc, *builder, *expr, symbolMap, defaults, intrinsics); + loc, *builder, *expr, localSymbols, defaults, intrinsics); } M::FuncOp genRuntimeFunction(RuntimeEntryCode rec, int kind) { @@ -580,7 +588,7 @@ class FirConverter { auto &name = std::get(stmt->t); assert(name.symbol); const auto &details{name.symbol->get()}; - M::Value *resultRef{symbolMap.lookupSymbol(details.result())}; + M::Value *resultRef{localSymbols.lookupSymbol(details.result())}; // FIXME: what happens if result was never referenced before and hence no // temp was created? assert(resultRef); @@ -673,10 +681,10 @@ class FirConverter { } void switchInsertionPointToWhere(fir::WhereOp &where) { - // FIXME + builder->setInsertionPointToStart(&where.whereRegion().front()); } void switchInsertionPointToOtherwise(fir::WhereOp &where) { - // FIXME + builder->setInsertionPointToStart(&where.otherRegion().front()); } template void handleCondition(fir::WhereOp &where, const A *stmt) { @@ -712,6 +720,7 @@ class FirConverter { } doLoop = builder->create( toLocation(), lo, hi, step); + builder->setInsertionPointToStart(doLoop.getBody()); }, [](const Pa::ScalarLogicalExpr &) { assert(false && "loop lacks iteration space"); @@ -721,6 +730,7 @@ class FirConverter { doLoop = builder->create(toLocation(), (M::Value *)nullptr, (M::Value *)nullptr, L::ArrayRef{}); + builder->setInsertionPointToStart(doLoop.getBody()); }, }, std::get>((*s)->t)->u); @@ -734,7 +744,6 @@ class FirConverter { } else if (std::holds_alternative(eval.u)) { // Construct fir.where fir::WhereOp where; - bool hasElse = false; for (auto &e : *eval.subs) { if (auto **s = std::get_if(&e.u)) { // fir.where op @@ -746,12 +755,8 @@ class FirConverter { } else if (std::holds_alternative(e.u)) { // otherwise block switchInsertionPointToOtherwise(where); - hasElse = true; } else if (std::holds_alternative(e.u)) { // close all open fir.where ops - if (!hasElse) { - switchInsertionPointToOtherwise(where); - } builder->clearInsertionPoint(); } else { genFIR(e); @@ -812,10 +817,10 @@ class FirConverter { void genFIR(const Pa::AssociateConstruct &) { TODO(); } void genFIR(const Pa::BlockConstruct &) { TODO(); } - void genFIR(const SelectCaseConstruct &) { TODO(); } void genFIR(const Pa::ChangeTeamConstruct &) { TODO(); } void genFIR(const Pa::CriticalConstruct &) { TODO(); } void genFIR(const Pa::DoConstruct &d) { +#if 0 auto &stmt{std::get>(d.t)}; const Pa::NonLabelDoStmt &ss{stmt.statement}; auto &ctrl{std::get>(ss.t)}; @@ -826,11 +831,15 @@ class FirConverter { // loop forever (See 11.1.7.4.1, para. 2) // pushDoContext(&ss); } - TODO(); +#endif + SOFT_TODO(); } - void genFIR(const Pa::IfConstruct &cst) { TODO(); } + void genFIR(const Pa::IfConstruct &) { SOFT_TODO(); } + + void genFIR(const SelectCaseConstruct &) { TODO(); } void genFIR(const SelectRankConstruct &) { TODO(); } void genFIR(const SelectTypeConstruct &) { TODO(); } + void genFIR(const Pa::WhereConstruct &) { TODO(); } /// Lower FORALL construct (See 10.2.4) @@ -1299,7 +1308,6 @@ class FirConverter { Pa::CharBlock currentPosition; CFGMapType cfgMap; std::list cfgEdgeSetPool; - SymMap symbolMap; AST::Evaluation *currentEvaluation; // FIXME: this is a hack public: From 6e2933709d5a0bc7cf1c06e3b9b495a2318d9c0e Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Fri, 6 Dec 2019 10:03:14 -0800 Subject: [PATCH 057/123] add some passes to bbc --- tools/bbc/CMakeLists.txt | 4 ++-- tools/bbc/{bbc.cc => bbc.cpp} | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) rename tools/bbc/{bbc.cc => bbc.cpp} (99%) diff --git a/tools/bbc/CMakeLists.txt b/tools/bbc/CMakeLists.txt index 3366006bfe55..a13b7b96d446 100644 --- a/tools/bbc/CMakeLists.txt +++ b/tools/bbc/CMakeLists.txt @@ -20,7 +20,7 @@ set(LIBS MLIRStandardOps ) -add_llvm_library(FirBbcLib bbc.cc) +add_llvm_library(FirBbcLib bbc.cpp) target_link_libraries(FirBbcLib ${LIBS} FIRTransforms) set(EXE_LIBS @@ -33,7 +33,7 @@ set(EXE_LIBS FortranBurnside ) -add_llvm_tool(bbc bbc.cc) +add_llvm_tool(bbc bbc.cpp) llvm_update_compile_flags(bbc) whole_archive_link(bbc ${LIBS}) target_link_libraries(bbc PRIVATE FIR MLIRIR FirBbcLib ${EXE_LIBS} LLVMSupport) diff --git a/tools/bbc/bbc.cc b/tools/bbc/bbc.cpp similarity index 99% rename from tools/bbc/bbc.cc rename to tools/bbc/bbc.cpp index cbe64733df58..d21bfbeea490 100644 --- a/tools/bbc/bbc.cc +++ b/tools/bbc/bbc.cpp @@ -38,6 +38,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" +#include "mlir/Conversion/LoopToStandard/ConvertLoopToStandard.h" #include "mlir/IR/MLIRContext.h" #include "mlir/IR/Module.h" #include "mlir/Parser.h" @@ -285,6 +286,9 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, pm.addPass(fir::createFIRToStdPass()); } if (driver.lowerToLLVM) { + pm.addPass(fir::createLowerToLoopPass()); + pm.addPass(fir::createFIRToStdPass()); + pm.addPass(mlir::createLowerToCFGPass()); pm.addPass(fir::createFIRToLLVMPass(nameMangler)); } if (driver.lowerToLLVMIR) { From 21cc68945fb57b2b836d83963a103c7822f30e72 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Thu, 5 Dec 2019 04:33:28 -0800 Subject: [PATCH 058/123] [PATCH 1/2] Intrinsic operations lowering tests and fixes + Implement TODO: better runtime function selection for intrinsics When no runtime function specialization exits for a type, select the "best" runtime version available that does not destroy information (e.g. narrows arguments). + Also change function mangling to match pgfortran/gfortran for external functions so that f18 compiled functions can be tested against. Use semantics::Symbol information to drive mangling (all required information for mangling regarding scopes and what the symbol is should be available inside this). + Better intrinsic function wrapper name in FIR Mangle argument types in name so that a difference can be made for cases where the result type is not sufficient (e.g. atan, power...) + Add expression evaluation test facility Conflicts: lib/burnside/bridge.cc Note: kept AST version only --- lib/burnside/builder.cc | 40 ++ lib/burnside/builder.h | 6 + lib/burnside/convert-expr.cc | 16 +- lib/burnside/intrinsics.cc | 166 +++++- test/burnside/expr-test-generator.cc | 689 ++++++++++++++++++++++ test/burnside/test_expression_lowering.sh | 65 ++ 6 files changed, 954 insertions(+), 28 deletions(-) create mode 100644 test/burnside/expr-test-generator.cc create mode 100755 test/burnside/test_expression_lowering.sh diff --git a/lib/burnside/builder.cc b/lib/burnside/builder.cc index e789d2843a31..9508b3398d1d 100644 --- a/lib/burnside/builder.cc +++ b/lib/burnside/builder.cc @@ -30,6 +30,46 @@ std::string B::applyNameMangling(llvm::StringRef parserName) { return "_Qp_"s + parserName.str(); } +std::string B::applyNameMangling(const evaluate::ProcedureDesignator &proc) { + if (const auto *symbol{proc.GetSymbol()}) { + return applyNameMangling(*symbol); + } else { + // Do not mangle intrinsic for now + assert(proc.GetSpecificIntrinsic() && + "expected intrinsic procedure in designator"); + return proc.GetName(); + } +} + +std::string B::applyNameMangling(semantics::SymbolRef symbol) { + // FIXME: this is fake for now, add type info, etc. + // For now, only works for external procedures + // TODO: apply binding + // TODO: determine if procedure are: + // - external, internal or module + // TODO: Apply proposed mangling with _Qp_ .... + return std::visit( + common::visitors{ + [&](const semantics::MainProgramDetails &) { return "MAIN_"s; }, + [&](const semantics::SubprogramDetails &) { + return symbol->name().ToString() + "_"; + }, + [&](const semantics::ProcEntityDetails &) { + return symbol->name().ToString() + "_"; + }, + [&](const semantics::SubprogramNameDetails &) { + assert(false && + "SubprogramNameDetails not expected after semantic analysis"); + return ""s; + }, + [&](const auto &) { + assert(false && "Symbol mangling TODO"); + return ""s; + }, + }, + symbol->details()); +} + mlir::FuncOp B::createFunction(mlir::ModuleOp module, llvm::StringRef name, mlir::FunctionType funcTy, parser::CookedSource const *cooked, parser::CharBlock const *cb) { diff --git a/lib/burnside/builder.h b/lib/burnside/builder.h index d340652840be..2eed75590d7f 100644 --- a/lib/burnside/builder.h +++ b/lib/burnside/builder.h @@ -31,6 +31,10 @@ namespace parser { class CookedSource; } +namespace evaluate { +struct ProcedureDesignator; +} + namespace burnside { /// Miscellaneous helper routines for building MLIR @@ -49,6 +53,8 @@ class SymMap { }; std::string applyNameMangling(llvm::StringRef parserName); +std::string applyNameMangling(const evaluate::ProcedureDesignator &proc); +std::string applyNameMangling(semantics::SymbolRef symbol); /// Get the current Module inline mlir::ModuleOp getModule(mlir::OpBuilder *bldr) { diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index e6a57e853aef..3ae2cc893edd 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -260,8 +260,18 @@ class ExprLowering { } template - M::Value *genval(Ev::Negate> const &) { - TODO(); + M::Value *genval(Ev::Negate> const &op) { + auto input{genval(op.left())}; + if constexpr (TC == IntegerCat) { + // Currently no Standard/FIR op for integer negation. + auto zero{genIntegerConstant(builder.getContext(), 0)}; + return builder.create(getLoc(), zero, input); + } else if constexpr (TC == RealCat) { + return builder.create(getLoc(), input); + } else { + static_assert(TC == ComplexCat, "Expected numeric type"); + return createBinaryOp(op); + } } template @@ -687,7 +697,7 @@ class ExprLowering { M::Type resultType{getFIRType(builder.getContext(), defaults, TC, KIND)}; M::FunctionType funTy{ M::FunctionType::get(argTypes, resultType, builder.getContext())}; - M::FuncOp func{getFunction(funRef.proc().GetName(), funTy)}; + M::FuncOp func{getFunction(applyNameMangling(funRef.proc()), funTy)}; auto call{builder.create(getLoc(), func, operands)}; // For now, Fortran return value are implemented with a single MLIR // function return value. diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index 5b231903d6b2..5f7487d20183 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -21,6 +21,7 @@ #include "fir/FIRType.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/Twine.h" +#include #include #include #include @@ -118,7 +119,7 @@ class IntrinsicLibrary::Implementation { /// Define the different FIR generators that can be mapped to intrinsic to /// generate the related code. - using Generator = mlir::Value *(Implementation::*)(Context &) const; + using Generator = mlir::Value *(Implementation::*)(Context &)const; /// Search a runtime function that is associated to the generic intrinsic name /// and whose signature matches the intrinsic arguments and result types. /// If no such runtime function is found but a runtime function associated @@ -276,29 +277,135 @@ mlir::FuncOp MathRuntimeLibrary::getFuncOp( return function; } +// This helper class computes a "distance" between two function types. +// The distance represents the argument and result conversions +// that are required if one use "to" instead of "from". +// Note that this is not a reflexive distance, and it does not +// define a total order at all. +class FunctionDistance { +public: + FunctionDistance() : infinite{true} {} + FunctionDistance(mlir::FunctionType from, mlir::FunctionType to) { + auto nInputs{from.getNumInputs()}; + auto nResults{from.getNumResults()}; + if (nResults != to.getNumResults() || nInputs != to.getNumInputs()) { + infinite = true; + } else { + for (unsigned int i{0}; i < nInputs; ++i) { + addArgumentDistance(from.getInput(i), to.getInput(i)); + } + for (unsigned int i{0}; i < nResults; ++i) { + addResultDistance(to.getResult(i), from.getResult(i)); + } + } + } + bool isSmallerThan(const FunctionDistance &d) const { + return d.infinite || + (!infinite && + std::lexicographical_compare(conversions.begin(), conversions.end(), + d.conversions.begin(), d.conversions.end())); + } + bool isLoosingPrecision() const { + return conversions[narrowingArg] != 0 || conversions[extendingResult] != 0; + } + bool isInfinite() const { return infinite; } + +private: + enum class Conversion { Forbidden, None, Narrow, Extend }; + + void addArgumentDistance(mlir::Type from, mlir::Type to) { + switch (conversionBetweenTypes(from, to)) { + case Conversion::Forbidden: infinite = true; break; + case Conversion::None: break; + case Conversion::Narrow: conversions[narrowingArg]++; break; + case Conversion::Extend: conversions[nonNarrowingArg]++; break; + } + } + void addResultDistance(mlir::Type from, mlir::Type to) { + switch (conversionBetweenTypes(from, to)) { + case Conversion::Forbidden: infinite = true; break; + case Conversion::None: break; + case Conversion::Narrow: conversions[nonExtendingResult]++; break; + case Conversion::Extend: conversions[extendingResult]++; break; + } + } + + static Conversion conversionBetweenTypes(mlir::Type from, mlir::Type to) { + if (from == to) { + return Conversion::None; + } + if (auto fromIntTy{from.dyn_cast()}) { + if (auto toIntTy{to.dyn_cast()}) { + return fromIntTy.getWidth() > toIntTy.getWidth() ? Conversion::Narrow + : Conversion::Extend; + } + } + if (auto fromRealTy{from.dyn_cast()}) { + if (auto toRealTy{to.dyn_cast()}) { + // FIXME: With the current runtime (no functions with f16 and bf16 + // types), and the current kinds to representation mapping, that + // just works, but that is not that simple: + // - conversions between f16 and bf16 destroy information in both + // ways. + // - No assumptions regarding kind <-> representation should be made + // here. + return fromRealTy.getFKind() > toRealTy.getFKind() ? Conversion::Narrow + : Conversion::Extend; + } + } + if (auto fromCplxTy{from.dyn_cast()}) { + if (auto toCplxTy{to.dyn_cast()}) { + // FIXME: Same as for Real. + return fromCplxTy.getFKind() > toCplxTy.getFKind() ? Conversion::Narrow + : Conversion::Extend; + } + } + // Notes: + // - No conversion between character types, specialization of runtime + // functions should be made instead. + // - It is not clear there is a use case for automatic conversions + // around Logical and it may damage hidden information in the physical + // storage so do not do it. + return Conversion::Forbidden; + } + + // Below are indexes to access data in conversions. + // The order in data does matter for lexicographical_compare + enum { + narrowingArg = 0, // usually bad + extendingResult, // usually bad + nonExtendingResult, // usually ok + nonNarrowingArg, // usually ok + dataSize + }; + std::array conversions{/* zero init*/}; + bool infinite{false}; // When forbidden conversion or wrong argument number +}; + +// Select runtime function that has the smallest distance to the intrinsic +// function type and that will not imply narrowing arguments or extending the +// result. llvm::Optional MathRuntimeLibrary::getFunction( mlir::OpBuilder &builder, llvm::StringRef name, mlir::FunctionType funcType) const { auto range{library.equal_range(name)}; const RuntimeFunction *bestNearMatch{nullptr}; + FunctionDistance bestMatchDistance{}; for (auto iter{range.first}; iter != range.second; ++iter) { const RuntimeFunction &impl{iter->second}; if (funcType == impl.type) { return getFuncOp(builder, impl); // exact match } else { - if (!bestNearMatch && - impl.type.getNumResults() == funcType.getNumResults() && - impl.type.getNumInputs() == funcType.getNumInputs()) { + FunctionDistance distance(funcType, impl.type); + if (distance.isSmallerThan(bestMatchDistance)) { bestNearMatch = &impl; - } else { - // TODO the best near match should not be the first hit. - // It should apply rules: - // -> Non narrowing argument conversion are better - // -> The "nearest" conversions are better + bestMatchDistance = std::move(distance); } } } if (bestNearMatch != nullptr) { + assert(!bestMatchDistance.isLoosingPrecision() && + "runtime selection looses precision"); return getFuncOp(builder, *bestNearMatch); } else { return {}; @@ -334,26 +441,35 @@ static mlir::FunctionType getFunctionType(mlir::Type resultType, argumentTypes, resultType, getModule(&builder).getContext()); } +// TODO find nicer type to string infra or move this in a mangling utility +// mlir as Type::dump(ostream) methods but it may adds ! +static std::string typeToString(mlir::Type t) { + if (auto i{t.dyn_cast()}) { + return "i" + std::to_string(i.getWidth()); + } else if (auto cplx{t.dyn_cast()}) { + return "z" + std::to_string(cplx.getFKind()); + } else if (auto real{t.dyn_cast()}) { + return "r" + std::to_string(real.getFKind()); + } else if (auto f{t.dyn_cast()}) { + return "f" + std::to_string(f.getWidth()); + } else if (auto logical{t.dyn_cast()}) { + return "l" + std::to_string(logical.getFKind()); + } else if (auto character{t.dyn_cast()}) { + return "c" + std::to_string(character.getFKind()); + } else { + assert(false && "no mangling for type"); + return ""s; + } +} static std::string getIntrinsicWrapperName( const llvm::StringRef &intrinsic, mlir::FunctionType funTy) { - // TODO find nicer type to string infra or move this in a mangling utility - auto addSuffix{[&](const llvm::Twine &suffix) -> std::string { - return ("fir." + intrinsic + suffix).str(); - }}; + std::string name{"fir." + intrinsic.str() + "."}; assert(funTy.getNumResults() == 1 && "only function mangling supported"); - mlir::Type resultType{funTy.getResult(0)}; - if (auto f{resultType.dyn_cast()}) { - return addSuffix(".f" + llvm::Twine(f.getWidth())); - } else if (auto i{resultType.dyn_cast()}) { - return addSuffix(".i" + llvm::Twine(i.getWidth())); - } else if (auto cplx{resultType.dyn_cast()}) { - return addSuffix(".c" + llvm::Twine(cplx.getFKind())); - } else if (auto real{resultType.dyn_cast()}) { - return addSuffix(".r" + llvm::Twine(real.getFKind())); - } else { - assert(false); - return addSuffix(".unknown"); + name += typeToString(funTy.getResult(0)); + for (std::size_t i{0}; i < funTy.getNumInputs(); ++i) { + name += "." + typeToString(funTy.getInput(i)); } + return name; } mlir::Value *IntrinsicLibrary::Implementation::outlineInWrapper( diff --git a/test/burnside/expr-test-generator.cc b/test/burnside/expr-test-generator.cc new file mode 100644 index 000000000000..96e6fad39298 --- /dev/null +++ b/test/burnside/expr-test-generator.cc @@ -0,0 +1,689 @@ +// Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include + +// Utility to generate Fortran expression evaluation tests. +// Given an expression "x op y", this utility can generates +// functions that evaluate the expression and takes x and y +// as argument and return the result. +// The utility allows to describe fortran intrinsic operations +// and their possible operand types in a table driven way. +// Functions to test all possible intrinsic operations can then be +// generated. +// The utility can also generate a driver function that calls the +// previous and compare the results with expectations +// (currently provided by calling a similar function compiled by a reference +// compiler). + +// TODO: Integrate with f18 libs to remove redundancies ? +// TODO: How could Peter's intrinsic table be used to generate +// intrinsic expression tests in a similar way ? + +enum class TypeCategory { Integer, Real, Complex, Logical, Character }; + +struct Type { + constexpr Type(TypeCategory c) : cat{c}, kind{} {} + constexpr Type(TypeCategory c, int k) : cat{c}, kind{k} {} + bool operator==(const Type &that) const { + return cat == that.cat && kind == that.kind; + } + bool operator!=(const Type &that) const { return !(*this == that); } + std::ostream &Dump(std::ostream &s) const { + switch (cat) { + case TypeCategory::Integer: s << "INTEGER"; break; + case TypeCategory::Real: s << "REAL"; break; + case TypeCategory::Logical: s << "LOGICAL"; break; + case TypeCategory::Complex: s << "COMPLEX"; break; + case TypeCategory::Character: s << "CHARACTER"; break; + } + if (kind) { + s << "(" << *kind << ")"; + } + return s; + } + + TypeCategory cat; + std::optional kind; // none = default +}; + +static constexpr Type DefaultReal{TypeCategory::Real}; +static constexpr Type Real4{TypeCategory::Real, 4}; +static constexpr Type Real8{TypeCategory::Real, 8}; +static constexpr Type DefaultInteger{TypeCategory::Integer}; +static constexpr Type Integer1{TypeCategory::Integer, 1}; +static constexpr Type Integer2{TypeCategory::Integer, 2}; +static constexpr Type Integer4{TypeCategory::Integer, 4}; +static constexpr Type Integer8{TypeCategory::Integer, 8}; +static constexpr Type DefaultComplex{TypeCategory::Complex}; +static constexpr Type Complex4{TypeCategory::Complex, 4}; +static constexpr Type Complex8{TypeCategory::Complex, 4}; +static constexpr Type DefaultLogical{TypeCategory::Logical}; +// Currently other types are not testable because existing Fortran +// compilers do not provide these types. + +enum class TypePattern { + OperandNumeric, + OperandLogical, + OperandComparable, + OperandOrdered +}; +enum class Constraint { None, NonZeroRHS, NoNegativeLHSWhenRealRHS }; + +struct Operation { + bool IsUnaryOp() const { return isUnaryOp; } + std::optional GetResultTypeForArguments(Type, Type) const; + std::optional GetResultTypeForArgument(Type) const; + const char *name; + const char *symbol; + TypePattern pattern; + Constraint constraint{Constraint::None}; + bool isUnaryOp{false}; +}; + +// Sorted by increasing precedence (not stricly increasing). See F2018 +// table 10.1. +static Operation operations[]{ + // TODO should be OK constexpr but g++ 8.2 not happy + // defined-binary-op cannot be described here. + {"eqv", ".EQV.", TypePattern::OperandLogical}, + {"neqv", ".NEQV.", TypePattern::OperandLogical}, + {"or", ".OR.", TypePattern::OperandLogical}, + {"and", ".AND.", TypePattern::OperandLogical}, + {"not", ".NOT.", TypePattern::OperandLogical, Constraint::None, true}, + {"eq", ".EQ.", TypePattern::OperandComparable}, + {"ne", ".NE.", TypePattern::OperandComparable}, + {"lt", ".LT.", TypePattern::OperandOrdered}, + {"le", ".LE.", TypePattern::OperandOrdered}, + {"gt", ".GT.", TypePattern::OperandOrdered}, + {"ge", ".GE.", TypePattern::OperandOrdered}, + // TODO: Concat + {"add", "+", TypePattern::OperandNumeric}, + {"sub", "-", TypePattern::OperandNumeric}, + {"minus", "-", TypePattern::OperandNumeric, Constraint::None, true}, + {"plus", "+", TypePattern::OperandNumeric, Constraint::None, true}, + {"mult", "*", TypePattern::OperandNumeric}, + {"div", "/", TypePattern::OperandNumeric, Constraint::NonZeroRHS}, + {"power", "**", TypePattern::OperandNumeric, + Constraint::NoNegativeLHSWhenRealRHS}, // TODO: check power pattern + // defined-unary-op cannot be described here. +}; + +static bool IsLogical(const Type t) { return t.cat == TypeCategory::Logical; } +static bool IsCharacter(const Type t) { + return t.cat == TypeCategory::Character; +} +static bool IsComplex(const Type t) { return t.cat == TypeCategory::Complex; } +static bool IsReal(const Type t) { return t.cat == TypeCategory::Real; } + +static bool IsOrderedNumeric(const Type t) { + return t.cat == TypeCategory::Real || t.cat == TypeCategory::Integer; +} + +static bool IsNumeric(const Type t) { + return t.cat == TypeCategory::Real || t.cat == TypeCategory::Integer || + t.cat == TypeCategory::Complex; +} + +std::optional Operation::GetResultTypeForArgument(Type t) const { + switch (pattern) { + case TypePattern::OperandNumeric: + if (IsNumeric(t)) { + return t; + } + break; + case TypePattern::OperandLogical: + if (IsLogical(t)) { + return t; + } + break; + case TypePattern::OperandComparable: + case TypePattern::OperandOrdered: + assert(false && "No unary comparisons"); + break; + } + return std::nullopt; +} + +static std::optional SelectBiggestKindForResult( + TypeCategory resultCat, Type lhs, Type rhs) { + if (!lhs.kind && !rhs.kind) { + return Type{resultCat}; + } else if (lhs.kind && rhs.kind) { + return Type{resultCat, std::max(*lhs.kind, *rhs.kind)}; + } else { + // Cannot know without more info about compiler what default compares to. + return std::nullopt; + } +} + +std::optional Operation::GetResultTypeForArguments( + Type lhs, Type rhs) const { + // See Fortran 2018 table 10.2 and section 10.1.9.3 + switch (pattern) { + case TypePattern::OperandNumeric: + if (!IsNumeric(rhs) || !IsNumeric(lhs)) { + return std::nullopt; + } + if (lhs.cat == rhs.cat) { + return SelectBiggestKindForResult(lhs.cat, lhs, rhs); + } + // different categories + if (IsComplex(lhs)) { + if (IsReal(rhs)) { + return SelectBiggestKindForResult(TypeCategory::Complex, lhs, rhs); + } + return lhs; + } + if (IsComplex(rhs)) { + if (IsReal(lhs)) { + return SelectBiggestKindForResult(TypeCategory::Complex, lhs, rhs); + } + return rhs; + } + if (IsReal(lhs)) { + // rhs must be integer + return lhs; + } + if (IsReal(rhs)) { + // lhs must be integer + return rhs; + } + assert(false && "no more operand cases"); + case TypePattern::OperandLogical: + if (IsLogical(rhs) && IsLogical(lhs)) { + return SelectBiggestKindForResult(TypeCategory::Logical, lhs, rhs); + } + break; + case TypePattern::OperandComparable: + if (IsNumeric(lhs) && IsNumeric(rhs)) { + return DefaultLogical; + } else if (IsCharacter(lhs) && IsCharacter(lhs) && lhs.kind == rhs.kind) { + return DefaultLogical; + } + break; + case TypePattern::OperandOrdered: + if (IsOrderedNumeric(lhs) && IsOrderedNumeric(rhs)) { + return DefaultLogical; + } else if (IsCharacter(lhs) && IsCharacter(lhs) && lhs.kind == rhs.kind) { + return DefaultLogical; + } + break; + } + return std::nullopt; +} + +// Test Plan Part + +// Only string list input for now +using Input = std::vector; +// TODO: more inputs method (e.g. random pick) + +enum class Eval { Constant, Dynamic }; +struct TestPlan { + std::vector operationToTests; + std::vector> inputs; + Eval referenceEvaluationMethod{Eval::Dynamic}; + Eval testEvaluationMethod{Eval::Dynamic}; +}; + +static inline std::string IndexedName(const char *name, std::size_t index) { + return std::string{name} + std::to_string(index); +} + +static constexpr auto passedName{"passed"}; +static constexpr auto failedName{"failed"}; + +struct CodeGenerator { + static constexpr auto varNameBase{"x"}; + static constexpr auto paramNameBase{"a"}; + static constexpr auto loopIndexNameBase{"i"}; + static constexpr auto testResultName{"test_res"}; + static constexpr auto refResultName{"ref_res"}; + + template + void GenerateEvaluationFunction( + const std::string &functionName, std::ostream &s) const { + GenerateEvaluationFunctionStmtAndSpec(functionName, s); + GenerateExpressionEvaluation(functionName, s); + s << "END FUNCTION" << std::endl; + s << std::endl; + } + + void GenerateEvaluationFunction(const std::string &functionNameBase, + std::ostream &s, Eval ev, bool isReference) const { + auto functionName{isReference ? GetReferenceFunctionName(functionNameBase) + : GetTestFunctionName(functionNameBase)}; + switch (ev) { + case Eval::Constant: + GenerateEvaluationFunction(functionName, s); + break; + case Eval::Dynamic: + GenerateEvaluationFunction(functionName, s); + break; + } + } + + void GenerateDriverSubroutine(const std::string &functionNameBase, + std::ostream &s, Eval refEval, Eval testEval) const { + s << "SUBROUTINE " << GetDriverName(functionNameBase) << "(" << passedName + << ", " << failedName << ")" << std::endl; + std::size_t numInputs{inputs.size()}; + assert(numInputs > 0 && "Expected non empty inputs in driver"); + if (refEval == Eval::Dynamic || testEval == Eval::Dynamic) { + // Inputs are required inside the driver only if one evaluation function + // is dynamic and inputs need to be passed to it from the driver. + for (std::size_t i{0}; i < numInputs; ++i) { + GenerateInputAsArrayParameter(i, s); + } + } + s << "INTEGER :: " << passedName << ", " << failedName << std::endl; + resultType.Dump(s) << " :: " << testResultName << ", " << refResultName + << std::endl; + + // TODO: Add a test number limit in cases there are many inputs ? This would + // need to be kept in sync with folding evaluation. + for (std::size_t i{0}; i < numInputs; ++i) { + s << "DO " << IndexedName(loopIndexNameBase, i) << " = 1," + << inputs[i]->second.size() << std::endl; + } + + // TODO: Handle constraints (e.g divide by zero). Would also need to be kept + // in sync with folding evaluation. + s << refResultName << " = "; + auto refFuncName{GetReferenceFunctionName(functionNameBase)}; + switch (refEval) { + case Eval::Dynamic: + GenerateEvaluationFunctionCall(refFuncName, s); + break; + case Eval::Constant: + GenerateEvaluationFunctionCall(refFuncName, s); + break; + } + s << testResultName << " = "; + auto testFuncName{GetTestFunctionName(functionNameBase)}; + switch (testEval) { + case Eval::Dynamic: + GenerateEvaluationFunctionCall(testFuncName, s); + break; + case Eval::Constant: + GenerateEvaluationFunctionCall(testFuncName, s); + break; + } + + s << "IF ("; + GenerateResultsComparison(s); + s << ") THEN" << std::endl; + s << passedName << " = " << passedName << " + 1" << std::endl; + s << "ELSE" << std::endl; + s << failedName << " = " << failedName << " + 1" << std::endl; + GenerateFailureMessage(functionNameBase, s); + s << "END IF" << std::endl; + + for (std::size_t i{0}; i < numInputs; ++i) { + s << "END DO" << std::endl; + } + s << "END SUBROUTINE" << std::endl; + s << std::endl; + } + + template + void GenerateEvaluationFunctionCall( + const std::string &functionName, std::ostream &s) const { + s << functionName << "("; + if constexpr (Ev == Eval::Dynamic) { + ApplyOnInputIndexes( + [&](std::size_t i) { + return GetArrayElement(paramNameBase, loopIndexNameBase, i); + }, + ", ", s); + } else { + static_assert(Ev == Eval::Constant, "unhandled evaluation method"); + ApplyOnInputIndexes( + [&](std::size_t i) { return IndexedName(loopIndexNameBase, i); }, + ", ", s); + } + s << ")" << std::endl; + } + + void GenerateFailureMessage( + const std::string &functionNameBase, std::ostream &s) const { + s << "PRINT *, \"FAILED " << functionNameBase << " test: \""; + for (std::size_t i{0}; i < inputs.size(); ++i) { + auto loopId{IndexedName(loopIndexNameBase, i)}; + s << ", \"" << loopId << " = \", " << loopId; + } + s << std::endl; + s << "PRINT *, \" expected \", " << refResultName << ", \" got: \"," + << testResultName << std::endl; + } + + void GenerateInputAsArrayParameter(std::size_t i, std::ostream &s) const { + const auto &literals{inputs[i]->second}; + const auto size{literals.size()}; + assert(size > 0 && "No actual literal input for test"); + inputs[i]->first.Dump(s) + << ", PARAMETER :: " << IndexedName(paramNameBase, i) << "(" << size + << ") = ["; + s << literals[0]; + for (std::size_t i{1}; i < size; ++i) { + s << ", " << literals[i]; + } + s << "]" << std::endl; + } + + void GenerateDriverCall( + const std::string &functionNameBase, std::ostream &s) const { + s << "CALL " << GetDriverName(functionNameBase) << "(" << passedName << ", " + << failedName << ")" << std::endl; + } + + template + void GenerateEvaluationFunctionStmtAndSpec( + const std::string &functionName, std::ostream &s) const { + resultType.Dump(s) << " FUNCTION " << functionName << "("; + assert(inputs.size() && "expect at least on argument"); + const auto &name{Ev == Eval::Constant ? loopIndexNameBase : varNameBase}; + s << IndexedName(name, 0); + for (std::size_t i{1}; i < inputs.size(); ++i) { + s << ", " << IndexedName(name, i); + } + s << ")" << std::endl; + std::size_t i{0}; + if constexpr (Ev == Eval::Constant) { + for (const auto *input : inputs) { + s << "INTEGER :: " << IndexedName(name, i++) << std::endl; + } + } else { + for (const auto *input : inputs) { + input->first.Dump(s) << " :: " << IndexedName(name, i++) << std::endl; + } + } + } + + template + void GenerateExpressionEvaluation( + const std::string &resultName, std::ostream &s) const { + if constexpr (Ev == Eval::Constant) { + GenerateConstantExpressionEvaluation(resultName, s); + } else { + static_assert( + Ev == Eval::Dynamic, "unhandled expression evaluation method"); + s << resultName << " = "; + GenerateExpression( + [&](std::size_t i) { return IndexedName(varNameBase, i); }, s); + s << std::endl; + } + } + + void GenerateConstantExpressionEvaluation( + const std::string &resultName, std::ostream &s) const { + // Declare constant inputs + for (std::size_t i{0}; i < inputs.size(); ++i) { + GenerateInputAsArrayParameter(i, s); + } + // Evaluate in a parameter array using ac-implied-do + auto cstResultName{IndexedName(paramNameBase, inputs.size())}; + assert(inputs.size() > 0 && "expected at least one operands"); + resultType.Dump(s) << " , PARAMETER :: " << cstResultName << "(*"; + for (std::size_t i{1}; i < inputs.size(); ++i) { + s << ", *"; + } + s << ") = RESHAPE ([("; + for (std::size_t i{1}; i < inputs.size(); ++i) { + s << "("; + } + + // Expression evaluation inside ac-implied-do + // TODO: how to handle constraints ? + GenerateExpression( + [&](std::size_t i) { + return GetArrayElement(paramNameBase, loopIndexNameBase, i); + }, + s); + + // ac-implied-do (opening ")" were emited before). + s << ", "; + ApplyOnInputIndexes( + [&](std::size_t i) { + return IndexedName(loopIndexNameBase, i) + " = 1," + + std::to_string(inputs[i]->second.size()) + ")"; + }, + ",", s); + + // RESHAPE SHAPE argument + s << "], ["; + ApplyOnInputIndexes( + [&](std::size_t i) { return inputs[i]->second.size(); }, ",", s); + s << "])" << std::endl; + + // dynamically fetch requested result from the constant array + s << resultName << " = " << cstResultName << "("; + ApplyOnInputIndexes( + [&](std::size_t i) { return IndexedName(loopIndexNameBase, i); }, ",", + s); + s << ")" << std::endl; + } + + template + void inline ApplyOnInputIndexes( + const T &callable, const std::string &sep, std::ostream &s) const { + auto size{inputs.size()}; + if (size != 0) { + s << callable(0); + } + for (std::size_t i{1}; i < size; ++i) { + s << ", " << callable(i); + } + } + + template + void inline GenerateExpression( + const T &operandGenerator, std::ostream &s) const { + if (inputs.size() == 1) { + s << op.symbol << " " << operandGenerator(0); + } else { + assert(inputs.size() == 2 && "expected binary opreation"); + s << operandGenerator(0) << " " << op.symbol << " " + << operandGenerator(1); + } + } + + void GenerateEvaluationFunctionInterface(const std::string &functionNameBase, + std::ostream &s, Eval ev, bool isReference) const { + auto functionName{isReference ? GetReferenceFunctionName(functionNameBase) + : GetTestFunctionName(functionNameBase)}; + switch (ev) { + case Eval::Constant: + GenerateEvaluationFunctionStmtAndSpec(functionName, s); + break; + case Eval::Dynamic: + GenerateEvaluationFunctionStmtAndSpec(functionName, s); + break; + } + s << "END FUNCTION" << std::endl; + } + + void GenerateResultsComparison(std::ostream &s) const { + const auto *compareOp{ + resultType.cat == TypeCategory::Logical ? ".EQV." : ".EQ."}; + if (resultType.cat == TypeCategory::Real) { + // TODO: This should not always be an absolute comparison (epsilon margin + // for fp.). Complex needs similar checks. + s << refResultName << compareOp << testResultName << " .OR. "; + s << "(IEEE_IS_NAN(" << refResultName << ") .AND. IEEE_IS_NAN(" + << testResultName << "))"; + } else { + s << refResultName << compareOp << testResultName; + } + } + + // a0(i0) and such + static std::string GetArrayElement( + const char *arrayNameBase, const char *indexNameBase, std::size_t i) { + return IndexedName(arrayNameBase, i) + "(" + IndexedName(indexNameBase, i) + + ")"; + } + + static std::string GetTestFunctionName(const std::string &functionNameBase) { + return functionNameBase + "_test"; + } + static std::string GetReferenceFunctionName( + const std::string &functionNameBase) { + return functionNameBase + "_ref"; + } + static std::string GetDriverName(const std::string &functionNameBase) { + return functionNameBase + "_driver"; + } + Type resultType; + const Operation &op; + std::vector *> inputs; +}; + +// Test Generator +struct TestGenerator { + + void GenerateTests(std::ostream &testFile, std::ostream &refFile) { + std::stringstream testContentStream; + std::stringstream testInterfaceStream; + std::stringstream referenceAndDriverContentStream; + std::stringstream programContentStream; + auto generateInBuffers{ + [&](const CodeGenerator &codeGen, const std::string &functionNameBase) { + codeGen.GenerateEvaluationFunction(functionNameBase, + testContentStream, plan.testEvaluationMethod, false); + codeGen.GenerateEvaluationFunctionInterface(functionNameBase, + testInterfaceStream, plan.testEvaluationMethod, false); + codeGen.GenerateEvaluationFunction(functionNameBase, + referenceAndDriverContentStream, plan.referenceEvaluationMethod, + true); + codeGen.GenerateDriverSubroutine(functionNameBase, + referenceAndDriverContentStream, plan.referenceEvaluationMethod, + plan.testEvaluationMethod); + codeGen.GenerateDriverCall(functionNameBase, programContentStream); + }}; + + for (const auto *opName : plan.operationToTests) { + const auto *op{GetOperation(opName)}; + assert(op && "Broken test plan: undefined operation"); + for (const auto &input1 : plan.inputs) { + if (op->IsUnaryOp()) { + if (auto resultType{op->GetResultTypeForArgument(input1.first)}) { + CodeGenerator codeGen{*resultType, *op, {&input1}}; + auto functionNameBase{GetDistinctFunctionNameBase(*op)}; + generateInBuffers(codeGen, functionNameBase); + } + } else { + for (const auto &input2 : plan.inputs) { + if (auto resultType{op->GetResultTypeForArguments( + input1.first, input2.first)}) { + CodeGenerator codeGen{*resultType, *op, {&input1, &input2}}; + auto functionNameBase{GetDistinctFunctionNameBase(*op)}; + generateInBuffers(codeGen, functionNameBase); + } + } + } + } + } + + // Organize generated code + testFile << "! Generated test file" << std::endl; + testFile << testContentStream.rdbuf(); + testFile << "! End of generated test file" << std::endl; + + refFile << "! Generated reference and driver " << std::endl; + refFile << "MODULE REFERENCE_AND_DRIVER" << std::endl; + refFile << "USE IEEE_ARITHMETIC" << std::endl; // for IEEE_IS_NAN + refFile << "INTERFACE" << std::endl; + refFile << testInterfaceStream.rdbuf(); + refFile << "END INTERFACE" << std::endl; + refFile << std::endl; + refFile << "CONTAINS" << std::endl; + refFile << referenceAndDriverContentStream.rdbuf(); + refFile << "END MODULE" << std::endl; + refFile << std::endl; + refFile << "PROGRAM EXPR_TEST" << std::endl; + refFile << "USE REFERENCE_AND_DRIVER" << std::endl; + refFile << "INTEGER :: " << passedName << ", " << failedName << std::endl; + refFile << passedName << " = 0 " << std::endl; + refFile << failedName << " = 0 " << std::endl; + refFile << programContentStream.rdbuf(); + refFile << "PRINT *, \"Passed: \"," << passedName << ", \" Failed: \", " + << failedName << std::endl; + refFile << "IF (" << failedName << ".GT. 0) ERROR STOP 1" << std::endl; + refFile << "END PROGRAM" << std::endl; + refFile << "! End of generated reference and driver" << std::endl; + } + + const Operation *GetOperation(const char *name) { + assert(name && "nullptr string for test name"); + for (const auto &op : operations) { + assert(op.name && "Broken operation"); + if (std::strcmp(name, op.name) == 0) { + return &op; + } + } + return nullptr; + } + + std::string GetDistinctFunctionNameBase(const Operation &op) { + return IndexedName(op.name, funcId++); + } + + TestPlan plan; + std::size_t funcId{0}; +}; + +// Test driver +int main(int argc, char **argv) { + Eval testEvalMethod{Eval::Dynamic}; + Eval refEvalMethod{Eval::Dynamic}; + int planId{0}; + for (int i{0}; i < argc; ++i) { + if (std::strcmp(argv[i], "test=folding") == 0) { + testEvalMethod = Eval::Constant; + } else if (std::strcmp(argv[i], "ref=folding") == 0) { + refEvalMethod = Eval::Constant; + } + } + + TestGenerator{ + { + {"eqv", "neqv", "or", "and", "not", "eq", "ne", "lt", "le", "gt", + "ge", "add", "sub", "minus", "plus", "mult", "div", "power"}, + { + {Integer1, {"-1_1", "12_1", "2_1"}}, + {Integer2, {"-1_2", "12500_2", "2_2"}}, + {Integer4, {"31000_4", "-64354_4"}}, + {Integer8, {"3000001_8", "-2654637545_8"}}, + {Real4, {"1.03687448_4", "3.1254641_4"}}, + {Real8, {"1.036874168446448_8", "3.12254533554641_8"}}, + // TODO: fix expression analysis bug with REAL(8)**COMPLEX(4) + // {Complex4, {"(-0.5_4,10.35544_4)", "(-5._4, 0.15647_4)"}}, + // {Complex8, + // {"(-0.5_8,10.35546579874_8)", + // "(-5.64654654_8, 0.155876974647_8)"}}, + {DefaultLogical, {".false.", ".true."}}, + }, + refEvalMethod, + testEvalMethod, + }} + .GenerateTests(std::cout, std::cerr); +} diff --git a/test/burnside/test_expression_lowering.sh b/test/burnside/test_expression_lowering.sh new file mode 100755 index 000000000000..f6389b6d3bde --- /dev/null +++ b/test/burnside/test_expression_lowering.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Test Fortran expression lowering by driving compilations +# and executions of programs generated by expr-test-generation.cc +# usage: cmd llvm_build_directory + +LLVM=$1 +BBC=$LLVM/bin/bbc +LLC=$LLVM/bin/llc +CPP=g++ +FCC=pgfortran # So far, only works with pgfortran because of pgmath linking. +FCC_OPTIONS="-Kieee" # That is required because bit to bit compare so far. +SRC=$PWD/expr-test-generator.cc + +function die { + echo "$(basename $0): $*" >&2 + exit 1 +} + +temp=`mktemp -d ./tmp.XXXXXX` +cd $temp +[[ $KEEP ]] || trap "cd .. && rm -rf $temp" EXIT + +testGen=./genTests +testFile=test.f90 +driverFile=driver.f90 +bbcLog=bbc.log +assembly=a.s +testObject=test.o +testExec=./test_exec +testLog=test.log + +$CPP $SRC -std=c++17 -o $testGen +[[ $? -ne 0 ]] && die "test generator compilation failure" +$testGen 1>$testFile 2>$driverFile +[[ $? -ne 0 ]] && die "test generation failure" +$BBC $testFile 2>$bbcLog +[[ $? -ne 0 ]] && die "bbc test.f90 compilation failure" +$LLC a.ll -o $assembly +[[ $? -ne 0 ]] && die "llc failed compiling bbc output" +as $assembly -o $testObject +[[ $? -ne 0 ]] && die "as failed compiling llc output" +$FCC $testObject $driverFile -o $testExec $FCC_OPTIONS +[[ $? -ne 0 ]] && die "driver.f90 compilation/linking failure" +$testExec > $testLog +result=$? +cat $testLog +if [ $result -ne 0 ]; then + echo "FAIL" +else + echo "PASS" +fi From bd7e7ce4c22126b7b17728b7a7b3f7d3f3890bff Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Fri, 6 Dec 2019 07:47:32 -0800 Subject: [PATCH 059/123] [PATCH 2/2] Fix rebase logical conflicts with new "ast" - Apply pgfortran mangling for external functions - Change expression lowering symbol map to the newly added `localSymbols` map. Also load return value from there. --- lib/burnside/bridge.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 172b99c504d0..bcd1a73690d7 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -538,8 +538,9 @@ class FirConverter { Se::Symbol const *&symbol) { setCurrentPosition(stmt.source); auto &n{std::get(stmt.statement.t)}; - name = n.ToString(); symbol = n.symbol; + assert(symbol && "Name resolution failure"); + name = applyNameMangling(*symbol); // FIXME: use NameMangler } void genFIR(const Pa::Statement &stmt, std::string &, Se::Symbol const *&) { @@ -550,8 +551,9 @@ class FirConverter { Se::Symbol const *&symbol) { setCurrentPosition(stmt.source); auto &n{std::get(stmt.statement.t)}; - name = n.ToString(); symbol = n.symbol; + assert(symbol && "Name resolution failure"); + name = applyNameMangling(*symbol); // FIXME: use NameMangler } void genFIR(const Pa::Statement &stmt, std::string &, Se::Symbol const *&) { From ccc774c2ed1be0dfd3b58ad3a681a33d50a21f81 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Fri, 6 Dec 2019 11:43:07 -0800 Subject: [PATCH 060/123] fix array ref --- lib/burnside/bridge.cc | 404 +++-------------------------------- lib/burnside/cfg-builder.h | 348 ++++++++++++++++++++++++++++++ lib/burnside/convert-expr.cc | 28 +-- 3 files changed, 389 insertions(+), 391 deletions(-) create mode 100644 lib/burnside/cfg-builder.h diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index bcd1a73690d7..32ec0f89bf9c 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -65,351 +65,8 @@ constexpr static bool isStopStmt(const Pa::StopStmt &stm) { return std::get(stm.t) == Pa::StopStmt::Kind::Stop; } -/// Traverse the AST and complete the CFG by drawing the arcs, pruning unused -/// potential targets, making implied jumps explicit, etc. -class CfgBuilder { - - AST::Evaluation *getEvalByLabel(const Pa::Label &label) { - auto iter = labels.find(label); - if (iter != labels.end()) { - return iter->second; - } - return nullptr; - } - - /// Collect all the potential targets and initialize them to unreferenced - void resetPotentialTargets(std::list &evals) { - for (auto &e : evals) { - if (e.isTarget) { - e.isTarget = false; - } - if (e.lab.has_value()) { - labels.try_emplace(*e.lab, &e); - } - if (e.subs) { - resetPotentialTargets(*e.subs); - } - } - } - - /// cache ASSIGN statements that may yield a live branch target - void cacheAssigns(std::list &evals) { - for (auto &e : evals) { - std::visit(Co::visitors{ - [&](const Pa::AssignStmt *stmt) { - auto *trg = getEvalByLabel(std::get(stmt->t)); - auto *sym = std::get(stmt->t).symbol; - assert(sym); - auto jter = assignedGotoMap.find(sym); - if (jter == assignedGotoMap.end()) { - std::list lst = {trg}; - assignedGotoMap.try_emplace(sym, lst); - } else { - jter->second.emplace_back(trg); - } - }, - [](auto) { /* do nothing */ }, - }, - e.u); - if (e.subs) { - cacheAssigns(*e.subs); - } - } - } - - void deannotate(std::list &evals) { - for (auto &e : evals) { - e.cfg = AST::CFGAnnotation::None; - if (e.subs) { - deannotate(*e.subs); - } - } - } - - bool structuredLoop(const std::optional &optLoopCtrl) { - if (optLoopCtrl.has_value()) - return std::visit( - Co::visitors{ - [](const Pa::LoopControl::Bounds &) { return true; }, - [](const Pa::ScalarLogicalExpr &) { return false; }, - [](const Pa::LoopControl::Concurrent &) { return true; }, - }, - optLoopCtrl->u); - return false; - } - - bool structuredCheck(std::list &evals) { - for (auto &e : evals) { - if (auto **s = std::get_if(&e.u)) { - if (!structuredLoop(std::get>( - std::get>((*s)->t) - .statement.t))) - return false; - return structuredCheck(*e.subs); - } - if (std::holds_alternative(e.u)) { - return structuredCheck(*e.subs); - } - if (e.subs) { - return false; - } - switch (e.cfg) { - case AST::CFGAnnotation::None: break; - case AST::CFGAnnotation::CondGoto: break; - case AST::CFGAnnotation::Iterative: break; - case AST::CFGAnnotation::FirStructuredOp: break; - case AST::CFGAnnotation::IndGoto: return false; - case AST::CFGAnnotation::IoSwitch: return false; - case AST::CFGAnnotation::Switch: return false; - case AST::CFGAnnotation::Return: return false; - case AST::CFGAnnotation::Terminate: return false; - case AST::CFGAnnotation::Goto: - if (!std::holds_alternative(e.u)) { - return false; - } - break; - } - } - return true; - } - - void wrapIterationSpaces(std::list &evals) { - for (auto &e : evals) { - if (std::holds_alternative(e.u)) - if (structuredCheck(*e.subs)) { - deannotate(*e.subs); - e.cfg = AST::CFGAnnotation::FirStructuredOp; - continue; - } - if (std::holds_alternative(e.u)) - if (structuredCheck(*e.subs)) { - deannotate(*e.subs); - e.cfg = AST::CFGAnnotation::FirStructuredOp; - continue; - } - // FIXME: ForallConstruct? WhereConstruct? - if (e.subs) { - wrapIterationSpaces(*e.subs); - } - } - } - - /// Add source->sink edge to CFG map - void addSourceToSink(AST::Evaluation *src, AST::Evaluation *snk) { - auto iter = cfgMap.find(src); - if (iter == cfgMap.end()) { - CFGSinkListType sink{snk}; - cfgEdgeSetPool.emplace_back(std::move(sink)); - auto rc{cfgMap.try_emplace(src, &cfgEdgeSetPool.back())}; - assert(rc.second && "insert failed unexpectedly"); - (void)rc; // for release build - return; - } - for (auto *s : *iter->second) - if (s == snk) { - return; - } - iter->second->push_back(snk); - } - - void addSourceToSink(AST::Evaluation *src, const Pa::Label &label) { - auto iter = labels.find(label); - assert(iter != labels.end()); - addSourceToSink(src, iter->second); - } - - /// Find the next ELSE IF, ELSE or END IF statement in the list - template A nextFalseTarget(A iter, const A &endi) { - for (; iter != endi; ++iter) - if (std::visit(Co::visitors{ - [&](const Pa::ElseIfStmt *) { return true; }, - [&](const Pa::ElseStmt *) { return true; }, - [&](const Pa::EndIfStmt *) { return true; }, - [](auto) { return false; }, - }, - iter->u)) { - break; - } - return iter; - } - - /// Add branches for this IF block like construct. - /// Branch to the "true block", the "false block", and from the end of the - /// true block to the end of the construct. - template - void doNextIfBlock(std::list &evals, AST::Evaluation &e, - const A &iter, const A &endif) { - A i{iter}; - A j{nextFalseTarget(++i, endif)}; - auto *cstr = std::get(e.parent); - AST::CGJump jump{&*endif}; - A k{evals.insert(j, AST::Evaluation{std::move(jump), j->parent})}; - if (i == j) { - // block was empty, so adjust "true" target - i = k; - } - addSourceToSink(&*k, cstr); - addSourceToSink(&e, &*i); - addSourceToSink(&e, &*j); - } - - /// Determine which branch targets are reachable. The target map must - /// already be initialized. - void reachabilityAnalysis(std::list &evals) { - for (auto iter = evals.begin(); iter != evals.end(); ++iter) { - auto &e = *iter; - switch (e.cfg) { - case AST::CFGAnnotation::None: - // do nothing - does not impart control flow - break; - case AST::CFGAnnotation::Goto: - std::visit( - Co::visitors{ - [&](const Pa::CycleStmt *) { - // FIXME: deal with construct name - auto *cstr = std::get(e.parent); - addSourceToSink(&e, &cstr->subs->front()); - }, - [&](const Pa::ExitStmt *) { - // FIXME: deal with construct name - auto *cstr = std::get(e.parent); - addSourceToSink(&e, &cstr->subs->back()); - }, - [&](const Pa::GotoStmt *stmt) { addSourceToSink(&e, stmt->v); }, - [&](const Pa::EndDoStmt *) { - // the END DO is the loop exit landing pad - // insert a JUMP as the backedge right before the END DO - auto *cstr = std::get(e.parent); - AST::CGJump jump{&cstr->subs->front()}; - AST::Evaluation jumpEval{std::move(jump), iter->parent}; - evals.insert(iter, std::move(jumpEval)); - addSourceToSink(&e, &cstr->subs->front()); - }, - [&](const AST::CGJump &jump) { - addSourceToSink(&e, jump.target); - }, - [](auto) { assert(false && "unhandled GOTO case"); }, - }, - e.u); - break; - case AST::CFGAnnotation::CondGoto: - std::visit(Co::visitors{ - [&](const Pa::IfStmt *) { - // check if these are marked; they must targets here - auto i{iter}; - addSourceToSink(&e, &*(++i)); - addSourceToSink(&e, &*(++i)); - }, - [&](const Pa::IfThenStmt *) { - doNextIfBlock(evals, e, iter, evals.end()); - }, - [&](const Pa::ElseIfStmt *) { - doNextIfBlock(evals, e, iter, evals.end()); - }, - [](const Pa::WhereConstructStmt *stmt) { TODO(); }, - [](const Pa::MaskedElsewhereStmt *stmt) { TODO(); }, - [](auto) { assert(false && "unhandled CGOTO case"); }, - }, - e.u); - break; - case AST::CFGAnnotation::IndGoto: - std::visit( - Co::visitors{ - [&](const Pa::AssignedGotoStmt *stmt) { - auto *sym = std::get(stmt->t).symbol; - if (assignedGotoMap.find(sym) != assignedGotoMap.end()) - for (auto *x : assignedGotoMap[sym]) { - addSourceToSink(&e, x); - } - for (auto &l : std::get>(stmt->t)) { - addSourceToSink(&e, l); - } - }, - [](auto) { assert(false && "unhandled IGOTO case"); }, - }, - e.u); - break; - case AST::CFGAnnotation::IoSwitch: - std::visit( - Co::visitors{ - [](const Pa::BackspaceStmt *stmt) { TODO(); }, - [](const Pa::CloseStmt *stmt) { TODO(); }, - [](const Pa::EndfileStmt *stmt) { TODO(); }, - [](const Pa::FlushStmt *stmt) { TODO(); }, - [](const Pa::InquireStmt *stmt) { TODO(); }, - [](const Pa::OpenStmt *stmt) { TODO(); }, - [](const Pa::ReadStmt *stmt) { TODO(); }, - [](const Pa::RewindStmt *stmt) { TODO(); }, - [](const Pa::WaitStmt *stmt) { TODO(); }, - [](const Pa::WriteStmt *stmt) { TODO(); }, - [](auto) { assert(false && "unhandled IO switch case"); }, - }, - e.u); - break; - case AST::CFGAnnotation::Switch: - std::visit(Co::visitors{ - [](const Pa::CallStmt *stmt) { TODO(); }, - [](const Pa::ArithmeticIfStmt *stmt) { TODO(); }, - [](const Pa::ComputedGotoStmt *stmt) { TODO(); }, - [](const Pa::SelectCaseStmt *stmt) { TODO(); }, - [](const Pa::SelectRankStmt *stmt) { TODO(); }, - [](const Pa::SelectTypeStmt *stmt) { TODO(); }, - [](auto) { assert(false && "unhandled switch case"); }, - }, - e.u); - break; - case AST::CFGAnnotation::Iterative: - std::visit(Co::visitors{ - [](const Pa::NonLabelDoStmt *stmt) { TODO(); }, - [](const Pa::WhereStmt *stmt) { TODO(); }, - [](const Pa::ForallStmt *stmt) { TODO(); }, - [](const Pa::WhereConstruct *stmt) { TODO(); }, - [](const Pa::ForallConstructStmt *stmt) { TODO(); }, - [](auto) { assert(false && "unhandled loop case"); }, - }, - e.u); - break; - case AST::CFGAnnotation::FirStructuredOp: - // do not visit the subs - continue; - case AST::CFGAnnotation::Return: - // do nothing - exits the function - break; - case AST::CFGAnnotation::Terminate: - // do nothing - exits the function - break; - } - if (e.subs) { - reachabilityAnalysis(*e.subs); - } - } - } - - void setActualTargets(std::list &evals) { - for (auto &lst1 : cfgEdgeSetPool) - for (auto *e : lst1) { - e->isTarget = true; - } - } - - CFGMapType &cfgMap; - std::list &cfgEdgeSetPool; - - L::DenseMap labels; - std::map> assignedGotoMap; - -public: - CfgBuilder(CFGMapType &m, std::list &p) - : cfgMap{m}, cfgEdgeSetPool{p} {} - - void run(AST::FunctionLikeUnit &func) { - resetPotentialTargets(func.evals); - cacheAssigns(func.evals); - wrapIterationSpaces(func.evals); - reachabilityAnalysis(func.evals); - setActualTargets(func.evals); - } -}; +// CfgBuilder implementation +#include "cfg-builder.h" /// Converter from AST to FIR /// @@ -696,6 +353,12 @@ class FirConverter { switchInsertionPointToWhere(where); } + M::Value *genFIRLoopIndex(const Pa::ScalarExpr &x) { + return builder->create(toLocation(), + M::IndexType::get(&mlirContext), + createFIRExpr(toLocation(), Se::GetExpr(x))); + } + // Structured control op (fir.loop, fir.where) void genFIREvalStructuredOp(AST::Evaluation &eval) { // process the list of Evaluations @@ -708,33 +371,30 @@ class FirConverter { for (auto &e : *eval.subs) { if (auto **s = std::get_if(&e.u)) { // do bounds, fir.loop op - std::visit( - Co::visitors{ - [&](const Pa::LoopControl::Bounds &x) { - auto *lo = - createFIRExpr(toLocation(), Se::GetExpr(x.lower)); - auto *hi = - createFIRExpr(toLocation(), Se::GetExpr(x.upper)); - L::SmallVector step; - if (x.step.has_value()) { - step.emplace_back( - createFIRExpr(toLocation(), Se::GetExpr(*x.step))); - } - doLoop = builder->create( - toLocation(), lo, hi, step); - builder->setInsertionPointToStart(doLoop.getBody()); - }, - [](const Pa::ScalarLogicalExpr &) { - assert(false && "loop lacks iteration space"); - }, - [&](const Pa::LoopControl::Concurrent &x) { - // FIXME: can project a multi-dimensional space - doLoop = builder->create(toLocation(), - (M::Value *)nullptr, (M::Value *)nullptr, - L::ArrayRef{}); - builder->setInsertionPointToStart(doLoop.getBody()); - }, - }, + std::visit(Co::visitors{ + [&](const Pa::LoopControl::Bounds &x) { + M::Value *lo = genFIRLoopIndex(x.lower); + M::Value *hi = genFIRLoopIndex(x.upper); + L::SmallVector step; + if (x.step.has_value()) { + step.emplace_back(createFIRExpr( + toLocation(), Se::GetExpr(*x.step))); + } + doLoop = builder->create( + toLocation(), lo, hi, step); + builder->setInsertionPointToStart(doLoop.getBody()); + }, + [](const Pa::ScalarLogicalExpr &) { + assert(false && "loop lacks iteration space"); + }, + [&](const Pa::LoopControl::Concurrent &x) { + // FIXME: can project a multi-dimensional space + doLoop = builder->create(toLocation(), + (M::Value *)nullptr, (M::Value *)nullptr, + L::ArrayRef{}); + builder->setInsertionPointToStart(doLoop.getBody()); + }, + }, std::get>((*s)->t)->u); } else if (std::holds_alternative(e.u)) { // close fir.loop op diff --git a/lib/burnside/cfg-builder.h b/lib/burnside/cfg-builder.h new file mode 100644 index 000000000000..a76b4edbd838 --- /dev/null +++ b/lib/burnside/cfg-builder.h @@ -0,0 +1,348 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_BRIDGE_CFG_BUILDER_H_ +#define FORTRAN_BURNSIDE_BRIDGE_CFG_BUILDER_H_ + +/// Traverse the AST and complete the CFG by drawing the arcs, pruning unused +/// potential targets, making implied jumps explicit, etc. +class CfgBuilder { + + AST::Evaluation *getEvalByLabel(const Pa::Label &label) { + auto iter = labels.find(label); + if (iter != labels.end()) { + return iter->second; + } + return nullptr; + } + + /// Collect all the potential targets and initialize them to unreferenced + void resetPotentialTargets(std::list &evals) { + for (auto &e : evals) { + if (e.isTarget) { + e.isTarget = false; + } + if (e.lab.has_value()) { + labels.try_emplace(*e.lab, &e); + } + if (e.subs) { + resetPotentialTargets(*e.subs); + } + } + } + + /// cache ASSIGN statements that may yield a live branch target + void cacheAssigns(std::list &evals) { + for (auto &e : evals) { + std::visit(Co::visitors{ + [&](const Pa::AssignStmt *stmt) { + auto *trg = getEvalByLabel(std::get(stmt->t)); + auto *sym = std::get(stmt->t).symbol; + assert(sym); + auto jter = assignedGotoMap.find(sym); + if (jter == assignedGotoMap.end()) { + std::list lst = {trg}; + assignedGotoMap.try_emplace(sym, lst); + } else { + jter->second.emplace_back(trg); + } + }, + [](auto) { /* do nothing */ }, + }, + e.u); + if (e.subs) { + cacheAssigns(*e.subs); + } + } + } + + void deannotate(std::list &evals) { + for (auto &e : evals) { + e.cfg = AST::CFGAnnotation::None; + if (e.subs) { + deannotate(*e.subs); + } + } + } + + bool structuredCheck(std::list &evals) { + for (auto &e : evals) { + if (auto **s = std::get_if(&e.u)) { + return (*s)->IsDoWhile() ? false : structuredCheck(*e.subs); + } + if (std::holds_alternative(e.u)) { + return structuredCheck(*e.subs); + } + if (e.subs) { + return false; + } + switch (e.cfg) { + case AST::CFGAnnotation::None: break; + case AST::CFGAnnotation::CondGoto: break; + case AST::CFGAnnotation::Iterative: break; + case AST::CFGAnnotation::FirStructuredOp: break; + case AST::CFGAnnotation::IndGoto: return false; + case AST::CFGAnnotation::IoSwitch: return false; + case AST::CFGAnnotation::Switch: return false; + case AST::CFGAnnotation::Return: return false; + case AST::CFGAnnotation::Terminate: return false; + case AST::CFGAnnotation::Goto: + if (!std::holds_alternative(e.u)) { + return false; + } + break; + } + } + return true; + } + + void wrapIterationSpaces(std::list &evals) { + for (auto &e : evals) { + if (std::holds_alternative(e.u)) + if (structuredCheck(*e.subs)) { + deannotate(*e.subs); + e.cfg = AST::CFGAnnotation::FirStructuredOp; + continue; + } + if (std::holds_alternative(e.u)) + if (structuredCheck(*e.subs)) { + deannotate(*e.subs); + e.cfg = AST::CFGAnnotation::FirStructuredOp; + continue; + } + // FIXME: ForallConstruct? WhereConstruct? + if (e.subs) { + wrapIterationSpaces(*e.subs); + } + } + } + + /// Add source->sink edge to CFG map + void addSourceToSink(AST::Evaluation *src, AST::Evaluation *snk) { + auto iter = cfgMap.find(src); + if (iter == cfgMap.end()) { + CFGSinkListType sink{snk}; + cfgEdgeSetPool.emplace_back(std::move(sink)); + auto rc{cfgMap.try_emplace(src, &cfgEdgeSetPool.back())}; + assert(rc.second && "insert failed unexpectedly"); + (void)rc; // for release build + return; + } + for (auto *s : *iter->second) + if (s == snk) { + return; + } + iter->second->push_back(snk); + } + + void addSourceToSink(AST::Evaluation *src, const Pa::Label &label) { + auto iter = labels.find(label); + assert(iter != labels.end()); + addSourceToSink(src, iter->second); + } + + /// Find the next ELSE IF, ELSE or END IF statement in the list + template A nextFalseTarget(A iter, const A &endi) { + for (; iter != endi; ++iter) + if (std::visit(Co::visitors{ + [&](const Pa::ElseIfStmt *) { return true; }, + [&](const Pa::ElseStmt *) { return true; }, + [&](const Pa::EndIfStmt *) { return true; }, + [](auto) { return false; }, + }, + iter->u)) { + break; + } + return iter; + } + + /// Add branches for this IF block like construct. + /// Branch to the "true block", the "false block", and from the end of the + /// true block to the end of the construct. + template + void doNextIfBlock(std::list &evals, AST::Evaluation &e, + const A &iter, const A &endif) { + A i{iter}; + A j{nextFalseTarget(++i, endif)}; + auto *cstr = std::get(e.parent); + AST::CGJump jump{&*endif}; + A k{evals.insert(j, AST::Evaluation{std::move(jump), j->parent})}; + if (i == j) { + // block was empty, so adjust "true" target + i = k; + } + addSourceToSink(&*k, cstr); + addSourceToSink(&e, &*i); + addSourceToSink(&e, &*j); + } + + /// Determine which branch targets are reachable. The target map must + /// already be initialized. + void reachabilityAnalysis(std::list &evals) { + for (auto iter = evals.begin(); iter != evals.end(); ++iter) { + auto &e = *iter; + switch (e.cfg) { + case AST::CFGAnnotation::None: + // do nothing - does not impart control flow + break; + case AST::CFGAnnotation::Goto: + std::visit( + Co::visitors{ + [&](const Pa::CycleStmt *) { + // FIXME: deal with construct name + auto *cstr = std::get(e.parent); + addSourceToSink(&e, &cstr->subs->front()); + }, + [&](const Pa::ExitStmt *) { + // FIXME: deal with construct name + auto *cstr = std::get(e.parent); + addSourceToSink(&e, &cstr->subs->back()); + }, + [&](const Pa::GotoStmt *stmt) { addSourceToSink(&e, stmt->v); }, + [&](const Pa::EndDoStmt *) { + // the END DO is the loop exit landing pad + // insert a JUMP as the backedge right before the END DO + auto *cstr = std::get(e.parent); + AST::CGJump jump{&cstr->subs->front()}; + AST::Evaluation jumpEval{std::move(jump), iter->parent}; + evals.insert(iter, std::move(jumpEval)); + addSourceToSink(&e, &cstr->subs->front()); + }, + [&](const AST::CGJump &jump) { + addSourceToSink(&e, jump.target); + }, + [](auto) { assert(false && "unhandled GOTO case"); }, + }, + e.u); + break; + case AST::CFGAnnotation::CondGoto: + std::visit(Co::visitors{ + [&](const Pa::IfStmt *) { + // check if these are marked; they must targets here + auto i{iter}; + addSourceToSink(&e, &*(++i)); + addSourceToSink(&e, &*(++i)); + }, + [&](const Pa::IfThenStmt *) { + doNextIfBlock(evals, e, iter, evals.end()); + }, + [&](const Pa::ElseIfStmt *) { + doNextIfBlock(evals, e, iter, evals.end()); + }, + [](const Pa::WhereConstructStmt *stmt) { TODO(); }, + [](const Pa::MaskedElsewhereStmt *stmt) { TODO(); }, + [](auto) { assert(false && "unhandled CGOTO case"); }, + }, + e.u); + break; + case AST::CFGAnnotation::IndGoto: + std::visit( + Co::visitors{ + [&](const Pa::AssignedGotoStmt *stmt) { + auto *sym = std::get(stmt->t).symbol; + if (assignedGotoMap.find(sym) != assignedGotoMap.end()) + for (auto *x : assignedGotoMap[sym]) { + addSourceToSink(&e, x); + } + for (auto &l : std::get>(stmt->t)) { + addSourceToSink(&e, l); + } + }, + [](auto) { assert(false && "unhandled IGOTO case"); }, + }, + e.u); + break; + case AST::CFGAnnotation::IoSwitch: + std::visit( + Co::visitors{ + [](const Pa::BackspaceStmt *stmt) { TODO(); }, + [](const Pa::CloseStmt *stmt) { TODO(); }, + [](const Pa::EndfileStmt *stmt) { TODO(); }, + [](const Pa::FlushStmt *stmt) { TODO(); }, + [](const Pa::InquireStmt *stmt) { TODO(); }, + [](const Pa::OpenStmt *stmt) { TODO(); }, + [](const Pa::ReadStmt *stmt) { TODO(); }, + [](const Pa::RewindStmt *stmt) { TODO(); }, + [](const Pa::WaitStmt *stmt) { TODO(); }, + [](const Pa::WriteStmt *stmt) { TODO(); }, + [](auto) { assert(false && "unhandled IO switch case"); }, + }, + e.u); + break; + case AST::CFGAnnotation::Switch: + std::visit(Co::visitors{ + [](const Pa::CallStmt *stmt) { TODO(); }, + [](const Pa::ArithmeticIfStmt *stmt) { TODO(); }, + [](const Pa::ComputedGotoStmt *stmt) { TODO(); }, + [](const Pa::SelectCaseStmt *stmt) { TODO(); }, + [](const Pa::SelectRankStmt *stmt) { TODO(); }, + [](const Pa::SelectTypeStmt *stmt) { TODO(); }, + [](auto) { assert(false && "unhandled switch case"); }, + }, + e.u); + break; + case AST::CFGAnnotation::Iterative: + std::visit(Co::visitors{ + [](const Pa::NonLabelDoStmt *stmt) { TODO(); }, + [](const Pa::WhereStmt *stmt) { TODO(); }, + [](const Pa::ForallStmt *stmt) { TODO(); }, + [](const Pa::WhereConstruct *stmt) { TODO(); }, + [](const Pa::ForallConstructStmt *stmt) { TODO(); }, + [](auto) { assert(false && "unhandled loop case"); }, + }, + e.u); + break; + case AST::CFGAnnotation::FirStructuredOp: + // do not visit the subs + continue; + case AST::CFGAnnotation::Return: + // do nothing - exits the function + break; + case AST::CFGAnnotation::Terminate: + // do nothing - exits the function + break; + } + if (e.subs) { + reachabilityAnalysis(*e.subs); + } + } + } + + void setActualTargets(std::list &evals) { + for (auto &lst1 : cfgEdgeSetPool) + for (auto *e : lst1) { + e->isTarget = true; + } + } + + CFGMapType &cfgMap; + std::list &cfgEdgeSetPool; + + L::DenseMap labels; + std::map> assignedGotoMap; + +public: + CfgBuilder(CFGMapType &m, std::list &p) + : cfgMap{m}, cfgEdgeSetPool{p} {} + + void run(AST::FunctionLikeUnit &func) { + resetPotentialTargets(func.evals); + cacheAssigns(func.evals); + wrapIterationSpaces(func.evals); + reachabilityAnalysis(func.evals); + setActualTargets(func.evals); + } +}; + +#endif // FORTRAN_BURNSIDE_BRIDGE_CFG_BUILDER_H_ diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 3ae2cc893edd..8dac27b24303 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -571,27 +571,17 @@ class ExprLowering { // Determine the result type after removing `dims` dimensions from the array // type `arrTy` M::Type genSubType(M::Type arrTy, unsigned dims) { - if (auto memRef{arrTy.dyn_cast()}) { - if (dims < memRef.getRank()) { - auto shape{memRef.getShape()}; - llvm::SmallVector newShape; - // TODO: should we really remove rows here? - for (unsigned i = dims, e = memRef.getRank(); i < e; ++i) { - newShape.push_back(shape[i]); - } - return M::MemRefType::get(newShape, memRef.getElementType()); - } - return memRef.getElementType(); - } auto unwrapTy{arrTy.cast().getEleTy()}; auto seqTy{unwrapTy.cast()}; auto shape = seqTy.getShape(); - if ((shape.size() > 0) && (dims < shape.size())) { - fir::SequenceType::Shape newBnds; - // follow Fortran semantics and remove columns - for (unsigned i = 0; i < dims; ++i) { - newBnds.push_back(shape[i]); - } + assert(shape.size() > 0 && "removing columns for sequence sans shape"); + assert(dims <= shape.size() && "removing more columns than exist"); + fir::SequenceType::Shape newBnds; + // follow Fortran semantics and remove columns (from right) + for (unsigned i = 0, e = shape.size() - dims; i < e; ++i) { + newBnds.push_back(shape[i]); + } + if (!newBnds.empty()) { return fir::SequenceType::get(newBnds, seqTy.getEleTy()); } return seqTy.getEleTy(); @@ -608,7 +598,7 @@ class ExprLowering { for (auto &subsc : aref.subscript()) { args.push_back(genval(subsc)); } - auto ty{genSubType(base->getType(), args.size() - 1)}; + auto ty{genSubType(base->getType(), args.size())}; ty = fir::ReferenceType::get(ty); return builder.create(getLoc(), ty, base, args); } From 6f5c52ccb9ae11116e093f7926eda1aaf9fd6afd Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Fri, 6 Dec 2019 14:35:22 -0800 Subject: [PATCH 061/123] Burnside: simple DO to fir.loop conversion --- include/fir/FIROps.h | 2 + include/fir/FIROps.td | 7 +- lib/burnside/bridge.cc | 174 ++++++++++++++++++++++------------- lib/burnside/builder.cc | 19 ++++ lib/burnside/builder.h | 9 +- lib/burnside/convert-expr.cc | 14 ++- lib/fir/FIROps.cpp | 5 + 7 files changed, 160 insertions(+), 70 deletions(-) diff --git a/include/fir/FIROps.h b/include/fir/FIROps.h index 37938137567c..d4d656c15d6a 100644 --- a/include/fir/FIROps.h +++ b/include/fir/FIROps.h @@ -139,6 +139,8 @@ mlir::ParseResult parseCmpcOp(mlir::OpAsmParser &parser, LoopOp getForInductionVarOwner(mlir::Value *val); +bool isReferenceLike(mlir::Type type); + } // namespace fir #endif // FIR_FIROPS_H diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index 981201b83be2..f127d78cd632 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -296,8 +296,11 @@ def fir_LoadOp : fir_OneResultOp<"load", []>, let builders = [OpBuilder< "Builder *builder, OperationState &result, Value *refVal", [{ - fir::ReferenceType refTy = - refVal->getType().cast(); + if (!refVal) { + mlir::emitError(result.location, "LoadOp has null argument"); + return; + } + fir::ReferenceType refTy = refVal->getType().cast(); result.addOperands(refVal); result.addTypes(refTy.getEleTy()); }] diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 32ec0f89bf9c..506bfe5e23ce 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -94,6 +94,7 @@ class FirConverter { loc, *builder, *expr, localSymbols, defaults, intrinsics); } + // TODO: we need a map for the various Fortran runtime entry points M::FuncOp genRuntimeFunction(RuntimeEntryCode rec, int kind) { return genFunctionFIR( getRuntimeEntryName(rec), getRuntimeEntryType(rec, mlirContext, kind)); @@ -106,7 +107,7 @@ class FirConverter { return createFunction(module, callee, funcTy); } - bool inMainProgram(AST::Evaluation *cstr) { + static bool inMainProgram(AST::Evaluation *cstr) { return std::visit( Co::visitors{ [](AST::FunctionLikeUnit *c) { return c->isMainProgram(); }, @@ -115,7 +116,7 @@ class FirConverter { }, cstr->parent); } - const Pa::SubroutineStmt *inSubroutine(AST::Evaluation *cstr) { + static const Pa::SubroutineStmt *inSubroutine(AST::Evaluation *cstr) { return std::visit( Co::visitors{ [](AST::FunctionLikeUnit *c) { return c->isSubroutine(); }, @@ -124,7 +125,7 @@ class FirConverter { }, cstr->parent); } - const Pa::FunctionStmt *inFunction(AST::Evaluation *cstr) { + static const Pa::FunctionStmt *inFunction(AST::Evaluation *cstr) { return std::visit( Co::visitors{ [](AST::FunctionLikeUnit *c) { return c->isFunction(); }, @@ -133,7 +134,7 @@ class FirConverter { }, cstr->parent); } - const Pa::MpSubprogramStmt *inMPSubp(AST::Evaluation *cstr) { + static const Pa::MpSubprogramStmt *inMPSubp(AST::Evaluation *cstr) { return std::visit( Co::visitors{ [](AST::FunctionLikeUnit *c) { return c->isMPSubp(); }, @@ -144,36 +145,37 @@ class FirConverter { } template - const Se::SomeExpr *getScalarExprOfTuple(const A &tuple) { + static const Se::SomeExpr *getScalarExprOfTuple(const A &tuple) { return Se::GetExpr(std::get(tuple)); } - template const Se::SomeExpr *getExprOfTuple(const A &tuple) { + template + static const Se::SomeExpr *getExprOfTuple(const A &tuple) { return Se::GetExpr(std::get(tuple)); } /// Get the condition expression for a CondGoto evaluation const Se::SomeExpr *getEvaluationCondition(AST::Evaluation &eval) { - return std::visit(Co::visitors{ - [&](const Pa::IfStmt *stmt) { - return getScalarExprOfTuple(stmt->t); - }, - [&](const Pa::IfThenStmt *stmt) { - return getScalarExprOfTuple(stmt->t); - }, - [&](const Pa::ElseIfStmt *stmt) { - return getScalarExprOfTuple(stmt->t); - }, - [&](const Pa::WhereConstructStmt *stmt) { - return getExprOfTuple(stmt->t); - }, - [&](const Pa::MaskedElsewhereStmt *stmt) { - return getExprOfTuple(stmt->t); - }, - [](auto) -> const Se::SomeExpr * { - assert( - false && "unexpected conditional branch case"); - return nullptr; - }, - }, + return std::visit( + Co::visitors{ + [&](const Pa::IfStmt *stmt) { + return getScalarExprOfTuple(stmt->t); + }, + [&](const Pa::IfThenStmt *stmt) { + return getScalarExprOfTuple(stmt->t); + }, + [&](const Pa::ElseIfStmt *stmt) { + return getScalarExprOfTuple(stmt->t); + }, + [&](const Pa::WhereConstructStmt *stmt) { + return getExprOfTuple(stmt->t); + }, + [&](const Pa::MaskedElsewhereStmt *stmt) { + return getExprOfTuple(stmt->t); + }, + [&](auto) -> const Se::SomeExpr * { + M::emitError(toLocation(), "unexpected conditional branch case"); + return nullptr; + }, + }, eval.u); } @@ -346,7 +348,7 @@ class FirConverter { builder->setInsertionPointToStart(&where.otherRegion().front()); } template - void handleCondition(fir::WhereOp &where, const A *stmt) { + void genWhereCondition(fir::WhereOp &where, const A *stmt) { auto *cond{createLogicalExprAsI1( toLocation(), Se::GetExpr(std::get(stmt->t)))}; where = builder->create(toLocation(), cond, true); @@ -359,10 +361,16 @@ class FirConverter { createFIRExpr(toLocation(), Se::GetExpr(x))); } - // Structured control op (fir.loop, fir.where) + /// Structured control op (`fir.loop`, `fir.where`) + /// + /// Convert a DoConstruct to a `fir.loop` op. + /// Convert an IfConstruct to a `fir.where` op. + /// void genFIREvalStructuredOp(AST::Evaluation &eval) { + // TODO: array expressions, FORALL, WHERE ... + // process the list of Evaluations - assert(eval.subs); + assert(eval.subs && "eval must have a body"); auto *insPt = builder->getInsertionBlock(); if (std::holds_alternative(eval.u)) { @@ -371,34 +379,45 @@ class FirConverter { for (auto &e : *eval.subs) { if (auto **s = std::get_if(&e.u)) { // do bounds, fir.loop op - std::visit(Co::visitors{ - [&](const Pa::LoopControl::Bounds &x) { - M::Value *lo = genFIRLoopIndex(x.lower); - M::Value *hi = genFIRLoopIndex(x.upper); - L::SmallVector step; - if (x.step.has_value()) { - step.emplace_back(createFIRExpr( - toLocation(), Se::GetExpr(*x.step))); - } - doLoop = builder->create( - toLocation(), lo, hi, step); - builder->setInsertionPointToStart(doLoop.getBody()); - }, - [](const Pa::ScalarLogicalExpr &) { - assert(false && "loop lacks iteration space"); - }, - [&](const Pa::LoopControl::Concurrent &x) { - // FIXME: can project a multi-dimensional space - doLoop = builder->create(toLocation(), - (M::Value *)nullptr, (M::Value *)nullptr, - L::ArrayRef{}); - builder->setInsertionPointToStart(doLoop.getBody()); - }, - }, + std::visit( + Co::visitors{ + [&](const Pa::LoopControl::Bounds &x) { + // create the fir.loop op + M::Value *lo = genFIRLoopIndex(x.lower); + M::Value *hi = genFIRLoopIndex(x.upper); + L::SmallVector step; + if (x.step.has_value()) { + step.emplace_back( + createFIRExpr(toLocation(), Se::GetExpr(*x.step))); + } + doLoop = builder->create( + toLocation(), lo, hi, step); + builder->setInsertionPointToStart(doLoop.getBody()); + auto *sym{x.name.thing.symbol}; + auto ty{ + translateSymbolToFIRType(&mlirContext, defaults, *sym)}; + // TODO: should push this cast down to the uses + auto cvt{builder->create( + toLocation(), ty, doLoop.getInductionVar())}; + localSymbols.pushShadowSymbol(*sym, cvt); + }, + [&](const Pa::ScalarLogicalExpr &) { + // we should never reach here + M::emitError(toLocation(), "loop lacks iteration space"); + }, + [&](const Pa::LoopControl::Concurrent &x) { + // FIXME: can project a multi-dimensional space + doLoop = builder->create(toLocation(), + (M::Value *)nullptr, (M::Value *)nullptr, + L::ArrayRef{}); + builder->setInsertionPointToStart(doLoop.getBody()); + }, + }, std::get>((*s)->t)->u); } else if (std::holds_alternative(e.u)) { // close fir.loop op builder->clearInsertionPoint(); + localSymbols.popShadowSymbol(); } else { genFIR(e); } @@ -409,11 +428,11 @@ class FirConverter { for (auto &e : *eval.subs) { if (auto **s = std::get_if(&e.u)) { // fir.where op - handleCondition(where, *s); + genWhereCondition(where, *s); } else if (auto **s = std::get_if(&e.u)) { // otherwise block, then nested fir.where switchInsertionPointToOtherwise(where); - handleCondition(where, *s); + genWhereCondition(where, *s); } else if (std::holds_alternative(e.u)) { // otherwise block switchInsertionPointToOtherwise(where); @@ -580,13 +599,12 @@ class FirConverter { // Statements that do not have control-flow semantics // - void genFIR(const Pa::AllocateStmt &) { TODO(); } + void genFIR(const Pa::AllocateStmt &) { SOFT_TODO(); } void genFIR(const Pa::AssignmentStmt &stmt) { auto *rhs{Se::GetExpr(std::get(stmt.t))}; auto *lhs{Se::GetExpr(std::get(stmt.t))}; - auto loc{toLocation()}; - builder->create( - loc, createFIRExpr(loc, rhs), createFIRAddr(loc, lhs)); + builder->create(toLocation(), + createFIRExpr(toLocation(), rhs), createFIRAddr(toLocation(), lhs)); } void genFIR(const Pa::BackspaceStmt &) { @@ -598,7 +616,7 @@ class FirConverter { TODO(); } void genFIR(const Pa::ContinueStmt &) {} // do nothing - void genFIR(const Pa::DeallocateStmt &) { TODO(); } + void genFIR(const Pa::DeallocateStmt &) { SOFT_TODO(); } void genFIR(const Pa::EndfileStmt &) { // call some IO runtime routine(s) TODO(); @@ -624,12 +642,36 @@ class FirConverter { // call some runtime routine TODO(); } - void genFIR(const Pa::NullifyStmt &) { TODO(); } + + /// Nullify pointer object list + /// + /// For each pointer object, reset the pointer to a disassociated status. + /// We do this by setting each pointer to null. + void genFIR(const Pa::NullifyStmt &stmt) { + for (auto &po : stmt.v) { + std::visit(Co::visitors{ + [&](const Pa::Name &sym) { + auto ty{translateSymbolToFIRType( + &mlirContext, defaults, *sym.symbol)}; + auto load{builder->create(toLocation(), + localSymbols.lookupSymbol(*sym.symbol))}; + auto idxTy{M::IndexType::get(&mlirContext)}; + auto zero{builder->create(toLocation(), + idxTy, builder->getIntegerAttr(idxTy, 0))}; + auto cast{builder->create( + toLocation(), ty, zero)}; + builder->create(toLocation(), cast, load); + }, + [&](const Pa::StructureComponent &) { TODO(); }, + }, + po.u); + } + } void genFIR(const Pa::OpenStmt &) { // call some IO runtime routine(s) TODO(); } - void genFIR(const Pa::PointerAssignmentStmt &) { TODO(); } + void genFIR(const Pa::PointerAssignmentStmt &) { SOFT_TODO(); } void genFIR(const Pa::PrintStmt &stmt) { L::SmallVector args; @@ -715,7 +757,7 @@ class FirConverter { } else if (auto *stmt = inMPSubp(currentEvaluation)) { genFIRProcedureExit(stmt); } else { - assert(false && "unknown subprogram type"); + M::emitError(toLocation(), "unknown subprogram type"); } } @@ -940,7 +982,7 @@ class FirConverter { // FIXME // return builder->create(lhs->getLoc(), ); } - assert(false && "cannot generate operation on this type"); + M::emitError(toLocation(), "cannot generate operation on this type"); return {}; } M::Value *genGE(M::Value *lhs, M::Value *rhs) { diff --git a/lib/burnside/builder.cc b/lib/burnside/builder.cc index 9508b3398d1d..e768ad0ecec4 100644 --- a/lib/burnside/builder.cc +++ b/lib/burnside/builder.cc @@ -93,3 +93,22 @@ mlir::Value *B::SymMap::lookupSymbol(semantics::SymbolRef symbol) { auto iter{symbolMap.find(&*symbol)}; return (iter == symbolMap.end()) ? nullptr : iter->second; } + +void B::SymMap::pushShadowSymbol( + semantics::SymbolRef symbol, mlir::Value *value) { + // find any existing mapping for symbol + auto iter{symbolMap.find(&*symbol)}; + const semantics::Symbol *sym{nullptr}; + mlir::Value *val{nullptr}; + // if mapping exists, save it on the shadow stack + if (iter != symbolMap.end()) { + sym = iter->first; + val = iter->second; + symbolMap.erase(iter); + } + shadowStack.emplace_back(sym, val); + // insert new shadow mapping + auto r{symbolMap.try_emplace(&*symbol, value)}; + assert(r.second && "unexpected insertion failure"); + (void)r; +} diff --git a/lib/burnside/builder.h b/lib/burnside/builder.h index 2eed75590d7f..9ac477e8211a 100644 --- a/lib/burnside/builder.h +++ b/lib/burnside/builder.h @@ -43,13 +43,20 @@ namespace burnside { class SymMap { llvm::DenseMap symbolMap; + std::vector> shadowStack; public: void addSymbol(semantics::SymbolRef symbol, mlir::Value *value); mlir::Value *lookupSymbol(semantics::SymbolRef symbol); - void clear() { symbolMap.clear(); } + void pushShadowSymbol(semantics::SymbolRef symbol, mlir::Value *value); + void popShadowSymbol() { shadowStack.pop_back(); } + + void clear() { + symbolMap.clear(); + shadowStack.clear(); + } }; std::string applyNameMangling(llvm::StringRef parserName); diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 8dac27b24303..5e46fdaa5747 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -70,7 +70,9 @@ class ExprLowering { M::OpBuilder &builder; SomeExpr const &expr; SymMap &symMap; - SymMap loadedSymbols{}; +#if 0 + SymMap loadedSymbols; +#endif Co::IntrinsicTypeDefaultKinds const &defaults; IntrinsicLibrary const &intrinsics; bool genLogicalAsI1{false}; @@ -231,6 +233,7 @@ class ExprLowering { M::Value *gendef(Se::SymbolRef sym) { return gen(sym); } M::Value *genval(Se::SymbolRef sym) { +#if 0 // Do not load the same symbols several time in one expression. // Fortran guarantees variable value must be the same wherever it // appears in one expression. @@ -241,6 +244,15 @@ class ExprLowering { loadedSymbols.addSymbol(sym, load); return load; } +#else + // Multiple loads should be CSEd. If they aren't, it's a bug in CSE that + // should not use a workaround here. + M::Value *var{gen(sym)}; + if (fir::isReferenceLike(var->getType())) { + return builder.create(getLoc(), var); + } + return var; +#endif } M::Value *genval(Ev::BOZLiteralConstant const &) { TODO(); } diff --git a/lib/fir/FIROps.cpp b/lib/fir/FIROps.cpp index 55e2da3cffee..b3653c777c98 100644 --- a/lib/fir/FIROps.cpp +++ b/lib/fir/FIROps.cpp @@ -582,6 +582,11 @@ void printUnaryOp(Operation *op, OpAsmPrinter &p) { p << " : " << op->getResult(0)->getType(); } +bool isReferenceLike(M::Type type) { + return type.isa() || type.isa() || + type.isa(); +} + // Tablegen operators #define GET_OP_CLASSES From 1a52ff3aeb1d35e383adcd888ce2a803e0cd2d89 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Mon, 9 Dec 2019 14:54:02 -0800 Subject: [PATCH 062/123] do a little work on calls --- lib/burnside/bridge.cc | 88 +++++++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 13 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 506bfe5e23ce..4c86689a063e 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -184,17 +184,17 @@ class FirConverter { // void genFIR(const Pa::Statement &stmt, std::string &name, - Se::Symbol const *&) { + const Se::Symbol *&) { setCurrentPosition(stmt.source); name = mangler.getProgramEntry(); } void genFIR(const Pa::Statement &stmt, std::string &, - Se::Symbol const *&) { + const Se::Symbol *&) { setCurrentPosition(stmt.source); genFIR(stmt.statement); } void genFIR(const Pa::Statement &stmt, std::string &name, - Se::Symbol const *&symbol) { + const Se::Symbol *&symbol) { setCurrentPosition(stmt.source); auto &n{std::get(stmt.statement.t)}; symbol = n.symbol; @@ -202,12 +202,12 @@ class FirConverter { name = applyNameMangling(*symbol); // FIXME: use NameMangler } void genFIR(const Pa::Statement &stmt, std::string &, - Se::Symbol const *&) { + const Se::Symbol *&) { setCurrentPosition(stmt.source); genFIR(stmt.statement); } void genFIR(const Pa::Statement &stmt, std::string &name, - Se::Symbol const *&symbol) { + const Se::Symbol *&symbol) { setCurrentPosition(stmt.source); auto &n{std::get(stmt.statement.t)}; symbol = n.symbol; @@ -215,19 +215,19 @@ class FirConverter { name = applyNameMangling(*symbol); // FIXME: use NameMangler } void genFIR(const Pa::Statement &stmt, std::string &, - Se::Symbol const *&) { + const Se::Symbol *&) { setCurrentPosition(stmt.source); genFIR(stmt.statement); } void genFIR(const Pa::Statement &stmt, - std::string &name, Se::Symbol const *&symbol) { + std::string &name, const Se::Symbol *&symbol) { setCurrentPosition(stmt.source); auto &n{stmt.statement.v}; name = n.ToString(); symbol = n.symbol; } void genFIR(const Pa::Statement &stmt, std::string &, - Se::Symbol const *&) { + const Se::Symbol *&) { setCurrentPosition(stmt.source); genFIR(stmt.statement); } @@ -472,12 +472,74 @@ class FirConverter { // No control-flow void genFIREvalNone(AST::Evaluation &eval) { genFIR(eval); } + M::FuncOp getFunc(L::StringRef name, M::FunctionType ty) { + if (auto func = getNamedFunction(module, name)) { + assert(func.getType() == ty); + return func; + } + return createFunction(module, name, ty); + } + + /// Lowering of CALL statement + /// + /// 1. Determine what function is being called/dispatched to + /// 2. Build a tuple of arguments to be passed to that function + /// 3. Emit fir.call/fir.dispatch on arguments void genFIR(const Pa::CallStmt &stmt) { - // FIXME handle alternate return - auto loc{toLocation()}; - (void)loc; - TODO(); + L::SmallVector argTy; + L::SmallVector resTy; + L::StringRef funName; + std::vector argsList; + setCurrentPosition(stmt.v.source); + std::visit(Co::visitors{ + [&](const Pa::Name &name) { + auto *sym = name.symbol; + auto n{sym->name()}; + funName = L::StringRef{n.begin(), n.size()}; + auto &details = sym->get(); + // TODO ProcEntityDetails? + // TODO bindName()? + argsList = details.dummyArgs(); + }, + [](const Pa::ProcComponentRef &) { TODO(); }, + }, + std::get(stmt.v.t).u); + for (auto *d : argsList) { + Se::SymbolRef sr{*d}; + // FIXME: + argTy.push_back(fir::ReferenceType::get( + translateSymbolToFIRType(&mlirContext, defaults, sr))); + } + auto funTy{M::FunctionType::get(argTy, resTy, builder->getContext())}; + // FIXME: mangle name + M::FuncOp func{getFunc(funName, funTy)}; + std::vector actuals; + for (auto &aa : std::get>(stmt.v.t)) { + auto &kw = std::get>(aa.t); + auto &arg = std::get(aa.t); + M::Value *fe{nullptr}; + std::visit(Co::visitors{ + [&](const Co::Indirection &e) { + auto *exp{Se::GetExpr(e)}; + // FIXME: needs to match argument, assumes trivial by-ref + fe = createFIRAddr(toLocation(), exp); + }, + [](const Pa::AltReturnSpec &) { TODO(); }, + [](const Pa::ActualArg::PercentRef &) { TODO(); }, + [](const Pa::ActualArg::PercentVal &) { TODO(); }, + }, + arg.u); + if (kw.has_value()) { + TODO(); + continue; + } + actuals.push_back(fe); + } + + builder->create( + toLocation(), resTy, builder->getSymbolRefAttr(funName), actuals); } + void genFIR(const Pa::IfStmt &) { TODO(); } void genFIR(const Pa::WaitStmt &) { TODO(); } void genFIR(const Pa::WhereStmt &) { TODO(); } @@ -1002,7 +1064,7 @@ class FirConverter { M::MLIRContext &mlirContext; const Pa::CookedSource *cooked; M::ModuleOp &module; - Co::IntrinsicTypeDefaultKinds const &defaults; + const Co::IntrinsicTypeDefaultKinds &defaults; IntrinsicLibrary intrinsics; M::OpBuilder *builder{nullptr}; fir::NameMangler &mangler; From f466af53caad810b0836b6ee7aea36b3c845a8db Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Mon, 9 Dec 2019 17:27:06 -0800 Subject: [PATCH 063/123] rebase changes; move IO statements --- lib/burnside/bridge.cc | 90 ++++++++++++++++------------------ lib/burnside/io.cc | 107 ++++++++++++++++++++++++++++++----------- lib/burnside/io.h | 46 +++++++++++++++--- lib/fir/Tilikum.cpp | 34 ++++++------- 4 files changed, 178 insertions(+), 99 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 4c86689a063e..baa47db943ac 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -661,6 +661,45 @@ class FirConverter { // Statements that do not have control-flow semantics // + // IO statements (see io.h) + + void genFIR(const Pa::BackspaceStmt &stmt) { + genBackspaceStatement(*builder, toLocation(), stmt); + } + void genFIR(const Pa::CloseStmt &stmt) { + genCloseStatement(*builder, toLocation(), stmt); + } + void genFIR(const Pa::EndfileStmt &stmt) { + genEndfileStatement(*builder, toLocation(), stmt); + } + void genFIR(const Pa::FlushStmt &stmt) { + genFlushStatement(*builder, toLocation(), stmt); + } + void genFIR(const Pa::OpenStmt &stmt) { + genOpenStatement(*builder, toLocation(), stmt); + } + void genFIR(const Pa::PrintStmt &stmt) { + llvm::SmallVector args; + for (auto &item : std::get>(stmt.t)) { + if (auto *parserExpr{std::get_if(&item.u)}) { + auto loc{toLocation(parserExpr->source)}; + args.push_back(createFIRExpr(loc, Se::GetExpr(*parserExpr))); + } else { + assert(false); // TODO implied do + } + } + genPrintStatement(*builder, toLocation(), args); + } + void genFIR(const Pa::ReadStmt &stmt) { + genReadStatement(*builder, toLocation(), stmt); + } + void genFIR(const Pa::RewindStmt &stmt) { + genRewindStatement(*builder, toLocation(), stmt); + } + void genFIR(const Pa::WriteStmt &stmt) { + genWriteStatement(*builder, toLocation(), stmt); + } + void genFIR(const Pa::AllocateStmt &) { SOFT_TODO(); } void genFIR(const Pa::AssignmentStmt &stmt) { auto *rhs{Se::GetExpr(std::get(stmt.t))}; @@ -669,20 +708,8 @@ class FirConverter { createFIRExpr(toLocation(), rhs), createFIRAddr(toLocation(), lhs)); } - void genFIR(const Pa::BackspaceStmt &) { - // call some IO runtime routine(s) - TODO(); - } - void genFIR(const Pa::CloseStmt &) { - // call some IO runtime routine(s) - TODO(); - } void genFIR(const Pa::ContinueStmt &) {} // do nothing void genFIR(const Pa::DeallocateStmt &) { SOFT_TODO(); } - void genFIR(const Pa::EndfileStmt &) { - // call some IO runtime routine(s) - TODO(); - } void genFIR(const Pa::EventPostStmt &) { // call some runtime routine TODO(); @@ -691,14 +718,10 @@ class FirConverter { // call some runtime routine TODO(); } - void genFIR(const Pa::FlushStmt &) { - // call some IO runtime routine(s) - TODO(); - } + void genFIR(const Pa::FormTeamStmt &) { TODO(); } - void genFIR(const Pa::InquireStmt &) { - // call some IO runtime routine(s) - TODO(); + void genFIR(const Pa::InquireStmt &stmt) { + genInquireStatement(*builder, toLocation(), stmt); } void genFIR(const Pa::LockStmt &) { // call some runtime routine @@ -729,33 +752,8 @@ class FirConverter { po.u); } } - void genFIR(const Pa::OpenStmt &) { - // call some IO runtime routine(s) - TODO(); - } void genFIR(const Pa::PointerAssignmentStmt &) { SOFT_TODO(); } - void genFIR(const Pa::PrintStmt &stmt) { - L::SmallVector args; - for (auto &item : std::get>(stmt.t)) { - if (auto *parserExpr{std::get_if(&item.u)}) { - auto loc{toLocation(parserExpr->source)}; - args.push_back(createFIRExpr(loc, Se::GetExpr(*parserExpr))); - } else { - TODO(); // implied do - } - } - genPrintStatement(*builder, toLocation(), args); - } - - void genFIR(const Pa::ReadStmt &) { - // call some IO runtime routine(s) - TODO(); - } - void genFIR(const Pa::RewindStmt &) { - // call some IO runtime routine(s) - TODO(); - } void genFIR(const Pa::SyncAllStmt &) { // call some runtime routine TODO(); @@ -777,10 +775,6 @@ class FirConverter { TODO(); } - void genFIR(const Pa::WriteStmt &) { - // call some IO runtime routine(s) - TODO(); - } void genFIR(const Pa::AssignStmt &) { TODO(); } void genFIR(const Pa::FormatStmt &) { TODO(); } void genFIR(const Pa::EntryStmt &) { TODO(); } diff --git a/lib/burnside/io.cc b/lib/burnside/io.cc index 038e022fa625..b8c3db23aa90 100644 --- a/lib/burnside/io.cc +++ b/lib/burnside/io.cc @@ -15,10 +15,19 @@ #include "io.h" #include "builder.h" #include "runtime.h" +#include "../parser/parse-tree.h" #include "mlir/Dialect/StandardOps/Ops.h" #include "mlir/IR/Builders.h" +#include -namespace Fortran::burnside { +namespace Br = Fortran::burnside; +namespace M = mlir; +namespace Pa = Fortran::parser; + +using namespace Fortran; +using namespace Fortran::burnside; + +namespace { /// Define actions to sort runtime functions. One actions /// may be associated to one or more runtime function. @@ -32,7 +41,7 @@ class IORuntimeDescription : public RuntimeStaticDescription { constexpr IORuntimeDescription( IOAction act, const char *s, MaybeTypeCode r, TypeCodeVector a) : RuntimeStaticDescription{s, r, a}, key{act} {} - static mlir::Type getIOCookieType(mlir::MLIRContext *context) { + static M::Type getIOCookieType(M::MLIRContext *context) { return getMLIRType(TypeCode::IOCookie, context); } IOAction key; @@ -66,7 +75,7 @@ static constexpr IORuntimeMap ioRuntimeMap{ioRuntimeTable}; /// exactly one runtime function. This constraint is enforced /// at compile time. This search is resolved at compile time. template -static mlir::FuncOp getIORuntimeFunction(mlir::OpBuilder &builder) { +static M::FuncOp getIORuntimeFunction(M::OpBuilder &builder) { static constexpr auto runtimeDescription{ioRuntimeMap.find(key)}; static_assert(runtimeDescription != ioRuntimeMap.end()); return runtimeDescription->getFuncOp(builder); @@ -76,18 +85,17 @@ static mlir::FuncOp getIORuntimeFunction(mlir::OpBuilder &builder) { /// are mapped to Output IOAction that must be mapped to at least one /// runtime function but can be mapped to more functions. /// This helper returns the function that has the same -/// mlir::FunctionType as the one seeked. It may therefore dynamically fail -/// if no function mapped to the Action has the seeked mlir::FunctionType. -static mlir::FuncOp getOutputRuntimeFunction( - mlir::OpBuilder &builder, mlir::Type type) { +/// M::FunctionType as the one seeked. It may therefore dynamically fail +/// if no function mapped to the Action has the seeked M::FunctionType. +static M::FuncOp getOutputRuntimeFunction(M::OpBuilder &builder, M::Type type) { static constexpr auto descriptionRange{ioRuntimeMap.getRange(IOA::Output)}; static_assert(!descriptionRange.empty()); - mlir::MLIRContext *context{getModule(&builder).getContext()}; - llvm::SmallVector argTypes{ + M::MLIRContext *context{getModule(&builder).getContext()}; + llvm::SmallVector argTypes{ IORuntimeDescription::getIOCookieType(context), type}; - mlir::FunctionType seekedType{mlir::FunctionType::get(argTypes, {}, context)}; + M::FunctionType seekedType{M::FunctionType::get(argTypes, {}, context)}; for (const auto &description : descriptionRange) { if (description.getMLIRFunctionType(context) == seekedType) { return description.getFuncOp(builder); @@ -97,34 +105,79 @@ static mlir::FuncOp getOutputRuntimeFunction( return {}; } +} // namespace + +void Br::genBackspaceStatement( + M::OpBuilder &, M::Location loc, const Pa::BackspaceStmt &) { + assert(false); +} + +void Br::genCloseStatement( + M::OpBuilder &, M::Location loc, const Pa::CloseStmt &) { + assert(false); +} + +void Br::genEndfileStatement( + M::OpBuilder &, M::Location loc, const Pa::EndfileStmt &) { + assert(false); +} + +void Br::genFlushStatement( + M::OpBuilder &, M::Location loc, const Pa::FlushStmt &) { + assert(false); +} + +void Br::genInquireStatement( + M::OpBuilder &, M::Location loc, const Pa::InquireStmt &) { + assert(false); +} + +void Br::genOpenStatement( + M::OpBuilder &, M::Location loc, const Pa::OpenStmt &) { + assert(false); +} + /// Lower print statement assuming a dummy runtime interface for now. -void genPrintStatement(mlir::OpBuilder &builder, mlir::Location loc, - llvm::ArrayRef args) { - mlir::ModuleOp module{getModule(&builder)}; - mlir::MLIRContext *mlirContext{module.getContext()}; +void Br::genPrintStatement( + M::OpBuilder &builder, M::Location loc, M::ValueRange args) { + M::ModuleOp module{getModule(&builder)}; + M::MLIRContext *mlirContext{module.getContext()}; - mlir::FuncOp beginFunc{ + M::FuncOp beginFunc{ getIORuntimeFunction(builder)}; // Initiate io - mlir::Type externalUnitType{mlir::IntegerType::get(32, mlirContext)}; - mlir::Value *defaultUnit{builder.create( + M::Type externalUnitType{M::IntegerType::get(32, mlirContext)}; + M::Value *defaultUnit{builder.create( loc, builder.getIntegerAttr(externalUnitType, 1))}; - llvm::SmallVector beginArgs{defaultUnit}; - mlir::Value *cookie{ - builder.create(loc, beginFunc, beginArgs).getResult(0)}; + llvm::SmallVector beginArgs{defaultUnit}; + M::Value *cookie{ + builder.create(loc, beginFunc, beginArgs).getResult(0)}; // Call data transfer runtime function - for (mlir::Value *arg : args) { - llvm::SmallVector operands{cookie, arg}; - mlir::FuncOp outputFunc{getOutputRuntimeFunction(builder, arg->getType())}; - builder.create(loc, outputFunc, operands); + for (M::Value *arg : args) { + llvm::SmallVector operands{cookie, arg}; + M::FuncOp outputFunc{getOutputRuntimeFunction(builder, arg->getType())}; + builder.create(loc, outputFunc, operands); } // Terminate IO - mlir::FuncOp endIOFunc{getIORuntimeFunction(builder)}; - llvm::SmallVector endArgs{cookie}; - builder.create(loc, endIOFunc, endArgs); + M::FuncOp endIOFunc{getIORuntimeFunction(builder)}; + llvm::SmallVector endArgs{cookie}; + builder.create(loc, endIOFunc, endArgs); +} + +void Br::genReadStatement( + M::OpBuilder &, M::Location loc, const Pa::ReadStmt &) { + assert(false); +} + +void Br::genRewindStatement( + M::OpBuilder &, M::Location loc, const Pa::RewindStmt &) { + assert(false); } +void Br::genWriteStatement( + M::OpBuilder &, M::Location loc, const Pa::WriteStmt &) { + assert(false); } diff --git a/lib/burnside/io.h b/lib/burnside/io.h index bae368609d9b..7f5d96d09b92 100644 --- a/lib/burnside/io.h +++ b/lib/burnside/io.h @@ -18,10 +18,22 @@ namespace mlir { class OpBuilder; class Location; -class Value; +class ValueRange; } -namespace llvm { -template class ArrayRef; + +namespace Fortran { + +namespace parser { +struct BackspaceStmt; +struct CloseStmt; +struct EndfileStmt; +struct FlushStmt; +struct InquireStmt; +struct OpenStmt; +struct PrintStmt; +struct ReadStmt; +struct RewindStmt; +struct WriteStmt; } /// Experimental IO lowering to FIR + runtime. The Runtime design is under @@ -30,9 +42,31 @@ template class ArrayRef; /// nodes and lower expressions as needed or should it get every expression /// already lowered as mlir::Value* ? (currently second options, not sure it /// will provide enough information for complex IO statements). -namespace Fortran::burnside { -void genPrintStatement( - mlir::OpBuilder &, mlir::Location loc, llvm::ArrayRef); +namespace burnside { + +class BridgeImpl; + +void genBackspaceStatement( + mlir::OpBuilder &, mlir::Location, const parser::BackspaceStmt &); +void genCloseStatement( + mlir::OpBuilder &, mlir::Location, const parser::CloseStmt &); +void genEndfileStatement( + mlir::OpBuilder &, mlir::Location, const parser::EndfileStmt &); +void genFlushStatement( + mlir::OpBuilder &, mlir::Location, const parser::FlushStmt &); +void genInquireStatement( + mlir::OpBuilder &, mlir::Location, const parser::InquireStmt &); +void genOpenStatement( + mlir::OpBuilder &, mlir::Location, const parser::OpenStmt &); +void genPrintStatement(mlir::OpBuilder &, mlir::Location, mlir::ValueRange); +void genReadStatement( + mlir::OpBuilder &, mlir::Location, const parser::ReadStmt &); +void genRewindStatement( + mlir::OpBuilder &, mlir::Location, const parser::RewindStmt &); +void genWriteStatement( + mlir::OpBuilder &, mlir::Location, const parser::WriteStmt &); + +} } #endif // FORTRAN_BURNSIDE_IO_H_ diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index f8147d848bff..38a42db10dca 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -1403,9 +1403,8 @@ void genCaseLadderStep(M::Location loc, M::Value *cmp, M::Block *dest, auto *newBlock = rewriter.createBlock(dest); rewriter.setInsertionPointToEnd(thisBlock); L::SmallVector dest_{dest, newBlock}; - L::SmallVector, 2> destOps_{destOps, {}}; - rewriter.create(loc, L::ArrayRef{cmp}, dest_, - destOps_); + L::SmallVector destOps_{destOps, {}}; + rewriter.create(loc, M::ValueRange{cmp}, dest_, destOps_); rewriter.setInsertionPointToEnd(newBlock); } @@ -1460,24 +1459,24 @@ struct SelectCaseOpConversion : public FIROpConversion { auto *newBlock2 = rewriter.createBlock(destinations[t]); rewriter.setInsertionPointToEnd(thisBlock); L::SmallVector dests{newBlock1, newBlock2}; - L::SmallVector, 2> destOps{{}, {}}; - rewriter.create(loc, L::ArrayRef{cmp}, - dests, destOps); + L::SmallVector destOps{{}, {}}; + rewriter.create(loc, M::ValueRange{cmp}, dests, + destOps); rewriter.setInsertionPointToEnd(newBlock1); - auto cmp2 = rewriter.create( + auto cmp_ = rewriter.create( loc, M::LLVM::ICmpPredicate::sle, selector, operands[nextOp++]); L::SmallVector dest2{destinations[t], newBlock2}; - L::SmallVector, 2> destOp2{destOperands[t], {}}; - rewriter.create(loc, L::ArrayRef{cmp2}, - dest2, destOp2); + L::SmallVector destOp2{destOperands[t], {}}; + rewriter.create(loc, M::ValueRange{cmp_}, dest2, + destOp2); rewriter.setInsertionPointToEnd(newBlock2); continue; } assert(attr.dyn_cast_or_null()); assert((t + 1 == conds) && "unit must be last"); - L::SmallVector empty; rewriter.replaceOpWithNewOp( - selectcase, empty, destinations[t], destOperands[t]); + selectcase, M::ValueRange{}, destinations[t], + M::ValueRange{destOperands[t]}); } return matchSuccess(); } @@ -1514,9 +1513,9 @@ void selectMatchAndRewrite(FIRToLLVMTypeConverter &lowering, M::Operation *op, } assert(attr.template dyn_cast_or_null()); assert((t + 1 == conds) && "unit must be last"); - L::SmallVector empty; - rewriter.replaceOpWithNewOp(select, empty, destinations[t], - destOperands[t]); + rewriter.replaceOpWithNewOp( + select, M::ValueRange{}, destinations[t], + M::ValueRange{destOperands[t]}); } } @@ -1643,10 +1642,9 @@ struct UnreachableOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto unreach = M::cast(op); - L::SmallVector destinations; // none - L::SmallVector destOperands; // none rewriter.replaceOpWithNewOp( - unreach, operands, destinations, destOperands, unreach.getAttrs()); + unreach, operands, L::ArrayRef{}, + L::ArrayRef{}, unreach.getAttrs()); return matchSuccess(); } }; From 449979604245c8f44aaf75539bbcdc349e5fc166 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 10 Dec 2019 13:47:08 -0800 Subject: [PATCH 064/123] introduce AbstractConverter --- lib/burnside/bridge.cc | 113 ++++++++++++++++++++--------------------- lib/burnside/bridge.h | 67 ++++++++++++++++++++++-- lib/burnside/io.cc | 86 +++++++++++++++++-------------- lib/burnside/io.h | 30 ++++------- 4 files changed, 176 insertions(+), 120 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index baa47db943ac..f255ff7476f2 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -72,7 +72,7 @@ constexpr static bool isStopStmt(const Pa::StopStmt &stm) { /// /// After building the AST and decorating it, the FirConverter processes that /// representation and lowers it to the FIR executable representation. -class FirConverter { +class FirConverter : public AbstractConverter { using LabelMapType = std::map; using Closure = std::function; @@ -357,8 +357,7 @@ class FirConverter { M::Value *genFIRLoopIndex(const Pa::ScalarExpr &x) { return builder->create(toLocation(), - M::IndexType::get(&mlirContext), - createFIRExpr(toLocation(), Se::GetExpr(x))); + M::IndexType::get(&mlirContext), genExprValue(*Se::GetExpr(x))); } /// Structured control op (`fir.loop`, `fir.where`) @@ -387,15 +386,13 @@ class FirConverter { M::Value *hi = genFIRLoopIndex(x.upper); L::SmallVector step; if (x.step.has_value()) { - step.emplace_back( - createFIRExpr(toLocation(), Se::GetExpr(*x.step))); + step.emplace_back(genExprValue(*Se::GetExpr(*x.step))); } doLoop = builder->create( toLocation(), lo, hi, step); builder->setInsertionPointToStart(doLoop.getBody()); auto *sym{x.name.thing.symbol}; - auto ty{ - translateSymbolToFIRType(&mlirContext, defaults, *sym)}; + auto ty{genType(*sym)}; // TODO: should push this cast down to the uses auto cvt{builder->create( toLocation(), ty, doLoop.getInductionVar())}; @@ -507,8 +504,7 @@ class FirConverter { for (auto *d : argsList) { Se::SymbolRef sr{*d}; // FIXME: - argTy.push_back(fir::ReferenceType::get( - translateSymbolToFIRType(&mlirContext, defaults, sr))); + argTy.push_back(fir::ReferenceType::get(genType(sr))); } auto funTy{M::FunctionType::get(argTy, resTy, builder->getContext())}; // FIXME: mangle name @@ -520,9 +516,8 @@ class FirConverter { M::Value *fe{nullptr}; std::visit(Co::visitors{ [&](const Co::Indirection &e) { - auto *exp{Se::GetExpr(e)}; // FIXME: needs to match argument, assumes trivial by-ref - fe = createFIRAddr(toLocation(), exp); + fe = genExprAddr(*Se::GetExpr(e)); }, [](const Pa::AltReturnSpec &) { TODO(); }, [](const Pa::ActualArg::PercentRef &) { TODO(); }, @@ -545,14 +540,14 @@ class FirConverter { void genFIR(const Pa::WhereStmt &) { TODO(); } void genFIR(const Pa::ComputedGotoStmt &stmt) { auto *exp{Se::GetExpr(std::get(stmt.t))}; - auto *e1{createFIRExpr(toLocation(), exp)}; + auto *e1{genExprValue(*exp)}; (void)e1; TODO(); } void genFIR(const Pa::ForallStmt &) { TODO(); } void genFIR(const Pa::ArithmeticIfStmt &stmt) { auto *exp{Se::GetExpr(std::get(stmt.t))}; - auto *e1{createFIRExpr(toLocation(), exp)}; + auto *e1{genExprValue(*exp)}; (void)e1; TODO(); } @@ -664,48 +659,24 @@ class FirConverter { // IO statements (see io.h) void genFIR(const Pa::BackspaceStmt &stmt) { - genBackspaceStatement(*builder, toLocation(), stmt); - } - void genFIR(const Pa::CloseStmt &stmt) { - genCloseStatement(*builder, toLocation(), stmt); - } - void genFIR(const Pa::EndfileStmt &stmt) { - genEndfileStatement(*builder, toLocation(), stmt); - } - void genFIR(const Pa::FlushStmt &stmt) { - genFlushStatement(*builder, toLocation(), stmt); - } - void genFIR(const Pa::OpenStmt &stmt) { - genOpenStatement(*builder, toLocation(), stmt); - } - void genFIR(const Pa::PrintStmt &stmt) { - llvm::SmallVector args; - for (auto &item : std::get>(stmt.t)) { - if (auto *parserExpr{std::get_if(&item.u)}) { - auto loc{toLocation(parserExpr->source)}; - args.push_back(createFIRExpr(loc, Se::GetExpr(*parserExpr))); - } else { - assert(false); // TODO implied do - } - } - genPrintStatement(*builder, toLocation(), args); - } - void genFIR(const Pa::ReadStmt &stmt) { - genReadStatement(*builder, toLocation(), stmt); - } - void genFIR(const Pa::RewindStmt &stmt) { - genRewindStatement(*builder, toLocation(), stmt); - } - void genFIR(const Pa::WriteStmt &stmt) { - genWriteStatement(*builder, toLocation(), stmt); - } + genBackspaceStatement(*this, stmt); + } + void genFIR(const Pa::CloseStmt &stmt) { genCloseStatement(*this, stmt); } + void genFIR(const Pa::EndfileStmt &stmt) { genEndfileStatement(*this, stmt); } + void genFIR(const Pa::FlushStmt &stmt) { genFlushStatement(*this, stmt); } + void genFIR(const Pa::InquireStmt &stmt) { genInquireStatement(*this, stmt); } + void genFIR(const Pa::OpenStmt &stmt) { genOpenStatement(*this, stmt); } + void genFIR(const Pa::PrintStmt &stmt) { genPrintStatement(*this, stmt); } + void genFIR(const Pa::ReadStmt &stmt) { genReadStatement(*this, stmt); } + void genFIR(const Pa::RewindStmt &stmt) { genRewindStatement(*this, stmt); } + void genFIR(const Pa::WriteStmt &stmt) { genWriteStatement(*this, stmt); } void genFIR(const Pa::AllocateStmt &) { SOFT_TODO(); } void genFIR(const Pa::AssignmentStmt &stmt) { auto *rhs{Se::GetExpr(std::get(stmt.t))}; auto *lhs{Se::GetExpr(std::get(stmt.t))}; - builder->create(toLocation(), - createFIRExpr(toLocation(), rhs), createFIRAddr(toLocation(), lhs)); + builder->create( + toLocation(), genExprValue(*rhs), genExprAddr(*lhs)); } void genFIR(const Pa::ContinueStmt &) {} // do nothing @@ -720,9 +691,6 @@ class FirConverter { } void genFIR(const Pa::FormTeamStmt &) { TODO(); } - void genFIR(const Pa::InquireStmt &stmt) { - genInquireStatement(*builder, toLocation(), stmt); - } void genFIR(const Pa::LockStmt &) { // call some runtime routine TODO(); @@ -736,8 +704,7 @@ class FirConverter { for (auto &po : stmt.v) { std::visit(Co::visitors{ [&](const Pa::Name &sym) { - auto ty{translateSymbolToFIRType( - &mlirContext, defaults, *sym.symbol)}; + auto ty{genType(*sym.symbol)}; auto load{builder->create(toLocation(), localSymbols.lookupSymbol(*sym.symbol))}; auto idxTy{M::IndexType::get(&mlirContext)}; @@ -866,15 +833,14 @@ class FirConverter { assert(details && "details for semantics::Symbol must be subprogram"); for (auto *a : details->dummyArgs()) { if (a) { // nullptr indicates alternate return argument - auto type{translateSymbolToFIRType(&mlirContext, defaults, *a)}; + auto type{genType(*a)}; args.push_back(fir::ReferenceType::get(type)); } } if (details->isFunction()) { // FIXME: handle subroutines that return magic values auto result{details->result()}; - results.push_back( - translateSymbolToFIRType(&mlirContext, defaults, result)); + results.push_back(genType(result)); } } auto funcTy{M::FunctionType::get(args, results, &mlirContext)}; @@ -1074,6 +1040,7 @@ class FirConverter { FirConverter() = delete; FirConverter(const FirConverter &) = delete; FirConverter &operator=(const FirConverter &) = delete; + virtual ~FirConverter() = default; explicit FirConverter(BurnsideBridge &bridge, fir::NameMangler &mangler) : mlirContext{bridge.getMLIRContext()}, cooked{bridge.getCookedSource()}, @@ -1104,6 +1071,36 @@ class FirConverter { u); } } + + // + // AbstractConverter overrides + + M::Value *genExprAddr( + const SomeExpr &expr, M::Location *loc = nullptr) override { + return createFIRAddr(loc ? *loc : toLocation(), &expr); + } + M::Value *genExprValue( + const SomeExpr &expr, M::Location *loc = nullptr) override { + return createFIRExpr(loc ? *loc : toLocation(), &expr); + } + + M::Type genType(const Ev::DataRef &data) override { + return translateDataRefToFIRType(&mlirContext, defaults, data); + } + M::Type genType(const SomeExpr &expr) override { + return translateSomeExprToFIRType(&mlirContext, defaults, &expr); + } + M::Type genType(const SymbolRef &sym) override { + return translateSymbolToFIRType(&mlirContext, defaults, sym); + } + + M::Location getCurrentLocation() override { return toLocation(); } + M::Location genLocation() override { return dummyLoc(mlirContext); } + M::Location genLocation(const Pa::CharBlock &block) override { + return parserPosToLoc(mlirContext, cooked, block); + } + + M::OpBuilder &getOpBuilder() override { return *builder; } }; } // namespace diff --git a/lib/burnside/bridge.h b/lib/burnside/bridge.h index 58207be1a542..0ec81b7d569e 100644 --- a/lib/burnside/bridge.h +++ b/lib/burnside/bridge.h @@ -24,26 +24,85 @@ /// /// [Coding style](https://llvm.org/docs/CodingStandards.html) -namespace Fortran::common { +namespace Fortran { +namespace common { class IntrinsicTypeDefaultKinds; +template class Reference; } - -namespace Fortran::parser { +namespace evaluate { +struct DataRef; +template class Expr; +struct SomeType; +} +namespace parser { +class CharBlock; class CookedSource; struct Program; } +namespace semantics { +class Symbol; +} +} // namespace Fortran namespace llvm { class Module; class SourceMgr; } - +namespace mlir { +class OpBuilder; +} namespace fir { struct NameMangler; } namespace Fortran::burnside { +using SomeExpr = evaluate::Expr; +using SymbolRef = common::Reference; + +/// The abstract interface for converter implementations to lower Fortran +/// front-end fragments such as expressions, types, etc. +class AbstractConverter { +public: + // + // Expressions + + /// Generate the address of the location holding the expression + virtual mlir::Value *genExprAddr( + const SomeExpr &, mlir::Location *loc = nullptr) = 0; + /// Generate the computations of the expression to produce a value + virtual mlir::Value *genExprValue( + const SomeExpr &, mlir::Location *loc = nullptr) = 0; + + // + // Types + + /// Generate the type of a DataRef + virtual mlir::Type genType(const evaluate::DataRef &) = 0; + /// Generate the type of an Expr + virtual mlir::Type genType(const SomeExpr &) = 0; + /// Generate the type of a Symbol + virtual mlir::Type genType(const SymbolRef &) = 0; + + // + // Locations + + /// Get the converter's current location + virtual mlir::Location getCurrentLocation() = 0; + /// Generate a dummy location + virtual mlir::Location genLocation() = 0; + /// Generate the location as converted from a CharBlock + virtual mlir::Location genLocation(const parser::CharBlock &) = 0; + + // + // FIR/MLIR + + /// Get the OpBuilder + virtual mlir::OpBuilder &getOpBuilder() = 0; + + virtual ~AbstractConverter() = default; +}; + class BurnsideBridge { public: static BurnsideBridge create( diff --git a/lib/burnside/io.cc b/lib/burnside/io.cc index b8c3db23aa90..3d4a847bf06d 100644 --- a/lib/burnside/io.cc +++ b/lib/burnside/io.cc @@ -13,9 +13,11 @@ // limitations under the License. #include "io.h" +#include "bridge.h" #include "builder.h" #include "runtime.h" #include "../parser/parse-tree.h" +#include "../semantics/tools.h" #include "mlir/Dialect/StandardOps/Ops.h" #include "mlir/IR/Builders.h" #include @@ -105,40 +107,8 @@ static M::FuncOp getOutputRuntimeFunction(M::OpBuilder &builder, M::Type type) { return {}; } -} // namespace - -void Br::genBackspaceStatement( - M::OpBuilder &, M::Location loc, const Pa::BackspaceStmt &) { - assert(false); -} - -void Br::genCloseStatement( - M::OpBuilder &, M::Location loc, const Pa::CloseStmt &) { - assert(false); -} - -void Br::genEndfileStatement( - M::OpBuilder &, M::Location loc, const Pa::EndfileStmt &) { - assert(false); -} - -void Br::genFlushStatement( - M::OpBuilder &, M::Location loc, const Pa::FlushStmt &) { - assert(false); -} - -void Br::genInquireStatement( - M::OpBuilder &, M::Location loc, const Pa::InquireStmt &) { - assert(false); -} - -void Br::genOpenStatement( - M::OpBuilder &, M::Location loc, const Pa::OpenStmt &) { - assert(false); -} - /// Lower print statement assuming a dummy runtime interface for now. -void Br::genPrintStatement( +void lowerPrintStatement( M::OpBuilder &builder, M::Location loc, M::ValueRange args) { M::ModuleOp module{getModule(&builder)}; M::MLIRContext *mlirContext{module.getContext()}; @@ -167,17 +137,55 @@ void Br::genPrintStatement( builder.create(loc, endIOFunc, endArgs); } -void Br::genReadStatement( - M::OpBuilder &, M::Location loc, const Pa::ReadStmt &) { +} // namespace + +void Br::genBackspaceStatement(AbstractConverter &, const Pa::BackspaceStmt &) { + assert(false); +} + +void Br::genCloseStatement(AbstractConverter &, const Pa::CloseStmt &) { + assert(false); +} + +void Br::genEndfileStatement(AbstractConverter &, const Pa::EndfileStmt &) { + assert(false); +} + +void Br::genFlushStatement(AbstractConverter &, const Pa::FlushStmt &) { + assert(false); +} + +void Br::genInquireStatement(AbstractConverter &, const Pa::InquireStmt &) { + assert(false); +} + +void Br::genOpenStatement(AbstractConverter &, const Pa::OpenStmt &) { + assert(false); +} + +void Br::genPrintStatement( + Br::AbstractConverter &converter, const Pa::PrintStmt &stmt) { + llvm::SmallVector args; + for (auto &item : std::get>(stmt.t)) { + if (auto *pe{std::get_if(&item.u)}) { + auto loc{converter.genLocation(pe->source)}; + args.push_back(converter.genExprValue(*semantics::GetExpr(*pe), &loc)); + } else { + assert(false); // TODO implied do + } + } + lowerPrintStatement( + converter.getOpBuilder(), converter.getCurrentLocation(), args); +} + +void Br::genReadStatement(AbstractConverter &, const Pa::ReadStmt &) { assert(false); } -void Br::genRewindStatement( - M::OpBuilder &, M::Location loc, const Pa::RewindStmt &) { +void Br::genRewindStatement(AbstractConverter &, const Pa::RewindStmt &) { assert(false); } -void Br::genWriteStatement( - M::OpBuilder &, M::Location loc, const Pa::WriteStmt &) { +void Br::genWriteStatement(AbstractConverter &, const Pa::WriteStmt &) { assert(false); } diff --git a/lib/burnside/io.h b/lib/burnside/io.h index 7f5d96d09b92..8a425529ca79 100644 --- a/lib/burnside/io.h +++ b/lib/burnside/io.h @@ -44,27 +44,19 @@ struct WriteStmt; /// will provide enough information for complex IO statements). namespace burnside { +class AbstractConverter; class BridgeImpl; -void genBackspaceStatement( - mlir::OpBuilder &, mlir::Location, const parser::BackspaceStmt &); -void genCloseStatement( - mlir::OpBuilder &, mlir::Location, const parser::CloseStmt &); -void genEndfileStatement( - mlir::OpBuilder &, mlir::Location, const parser::EndfileStmt &); -void genFlushStatement( - mlir::OpBuilder &, mlir::Location, const parser::FlushStmt &); -void genInquireStatement( - mlir::OpBuilder &, mlir::Location, const parser::InquireStmt &); -void genOpenStatement( - mlir::OpBuilder &, mlir::Location, const parser::OpenStmt &); -void genPrintStatement(mlir::OpBuilder &, mlir::Location, mlir::ValueRange); -void genReadStatement( - mlir::OpBuilder &, mlir::Location, const parser::ReadStmt &); -void genRewindStatement( - mlir::OpBuilder &, mlir::Location, const parser::RewindStmt &); -void genWriteStatement( - mlir::OpBuilder &, mlir::Location, const parser::WriteStmt &); +void genBackspaceStatement(AbstractConverter &, const parser::BackspaceStmt &); +void genCloseStatement(AbstractConverter &, const parser::CloseStmt &); +void genEndfileStatement(AbstractConverter &, const parser::EndfileStmt &); +void genFlushStatement(AbstractConverter &, const parser::FlushStmt &); +void genInquireStatement(AbstractConverter &, const parser::InquireStmt &); +void genOpenStatement(AbstractConverter &, const parser::OpenStmt &); +void genPrintStatement(AbstractConverter &, const parser::PrintStmt &); +void genReadStatement(AbstractConverter &, const parser::ReadStmt &); +void genRewindStatement(AbstractConverter &, const parser::RewindStmt &); +void genWriteStatement(AbstractConverter &, const parser::WriteStmt &); } } From 5ca3dd3e49516039ff360c2a1d9d604fd947bfec Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 10 Dec 2019 16:37:29 -0800 Subject: [PATCH 065/123] start threading name mangler fix parent bug remove lazy string building in name uniquer --- include/fir/InternalNames.h | 30 ++++----- lib/burnside/CMakeLists.txt | 1 + lib/burnside/bridge.cc | 49 ++++++++++----- lib/burnside/bridge.h | 2 + lib/burnside/builder.cc | 52 ++++++++------- lib/burnside/builder.h | 17 ++--- lib/burnside/convert-type.cc | 20 +----- lib/burnside/convert-type.h | 15 +---- lib/burnside/mangler.cc | 100 +++++++++++++++++++++++++++++ lib/burnside/mangler.h | 51 +++++++++++++++ lib/fir/InternalNames.cpp | 118 ++++++++++++++++++++--------------- 11 files changed, 313 insertions(+), 142 deletions(-) create mode 100644 lib/burnside/mangler.cc create mode 100644 lib/burnside/mangler.h diff --git a/include/fir/InternalNames.h b/include/fir/InternalNames.h index 48ceb870ebca..49637e26d47c 100644 --- a/include/fir/InternalNames.h +++ b/include/fir/InternalNames.h @@ -35,56 +35,56 @@ struct NameMangler { NameMangler() = default; /// Mangle a common block name - llvm::Twine doCommonBlock(llvm::StringRef name); + std::string doCommonBlock(llvm::StringRef name); /// Mangle a (global) constant name - llvm::Twine doConstant(llvm::ArrayRef modules, + std::string doConstant(llvm::ArrayRef modules, llvm::StringRef name); /// Mangle a dispatch table name - llvm::Twine doDispatchTable(llvm::ArrayRef modules, + std::string doDispatchTable(llvm::ArrayRef modules, llvm::Optional host, llvm::StringRef name, llvm::ArrayRef kinds); /// Mangle a compiler generated name - llvm::Twine doGenerated(llvm::StringRef name); + std::string doGenerated(llvm::StringRef name); /// Mangle an intrinsic type descriptor - llvm::Twine doIntrinsicTypeDescriptor(llvm::ArrayRef modules, + std::string doIntrinsicTypeDescriptor(llvm::ArrayRef modules, llvm::Optional host, IntrinsicType type, std::int64_t kind); /// Mangle a procedure name - llvm::Twine doProcedure(llvm::ArrayRef modules, + std::string doProcedure(llvm::ArrayRef modules, llvm::Optional host, llvm::StringRef name); /// Mangle a derived type name - llvm::Twine doType(llvm::ArrayRef modules, + std::string doType(llvm::ArrayRef modules, llvm::Optional host, llvm::StringRef name, llvm::ArrayRef kinds); /// Mangle a (derived) type descriptor name - llvm::Twine doTypeDescriptor(llvm::ArrayRef modules, + std::string doTypeDescriptor(llvm::ArrayRef modules, llvm::Optional host, llvm::StringRef name, llvm::ArrayRef kinds); /// Mangle a (global) variable name - llvm::Twine doVariable(llvm::ArrayRef modules, + std::string doVariable(llvm::ArrayRef modules, llvm::StringRef name); /// Entry point for the PROGRAM (called by the runtime) - constexpr static llvm::StringRef getProgramEntry() { - return "__MAIN"; + constexpr static llvm::StringRef doProgramEntry() { + return "MAIN_"; } private: - llvm::Twine addAsString(std::int64_t i); - llvm::Twine doKind(std::int64_t kind); - llvm::Twine doKinds(llvm::ArrayRef kinds); - llvm::Twine toLower(llvm::StringRef name); + llvm::StringRef addAsString(std::int64_t i); + std::string doKind(std::int64_t kind); + std::string doKinds(llvm::ArrayRef kinds); + llvm::StringRef toLower(llvm::StringRef name); llvm::StringSet<> cache; }; diff --git a/lib/burnside/CMakeLists.txt b/lib/burnside/CMakeLists.txt index cdbdfbffa227..9844363ac39e 100644 --- a/lib/burnside/CMakeLists.txt +++ b/lib/burnside/CMakeLists.txt @@ -21,6 +21,7 @@ add_library(FortranBurnside convert-expr.cc convert-type.cc intrinsics.cc + mangler.cc runtime.cc io.cc ) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index f255ff7476f2..5607189dc746 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -19,6 +19,7 @@ #include "convert-type.h" #include "intrinsics.h" #include "io.h" +#include "mangler.h" #include "runtime.h" #include "../parser/parse-tree.h" #include "../semantics/tools.h" @@ -94,6 +95,10 @@ class FirConverter : public AbstractConverter { loc, *builder, *expr, localSymbols, defaults, intrinsics); } + std::string mangledName(SymbolRef symbol) { + return mangle::mangleName(mangler, symbol); + } + // TODO: we need a map for the various Fortran runtime entry points M::FuncOp genRuntimeFunction(RuntimeEntryCode rec, int kind) { return genFunctionFIR( @@ -104,7 +109,7 @@ class FirConverter : public AbstractConverter { if (auto func{getNamedFunction(module, callee)}) { return func; } - return createFunction(module, callee, funcTy); + return createFunction(*this, callee, funcTy); } static bool inMainProgram(AST::Evaluation *cstr) { @@ -186,7 +191,7 @@ class FirConverter : public AbstractConverter { void genFIR(const Pa::Statement &stmt, std::string &name, const Se::Symbol *&) { setCurrentPosition(stmt.source); - name = mangler.getProgramEntry(); + name = mangler.doProgramEntry(); } void genFIR(const Pa::Statement &stmt, std::string &, const Se::Symbol *&) { @@ -199,7 +204,7 @@ class FirConverter : public AbstractConverter { auto &n{std::get(stmt.statement.t)}; symbol = n.symbol; assert(symbol && "Name resolution failure"); - name = applyNameMangling(*symbol); // FIXME: use NameMangler + name = mangledName(*symbol); } void genFIR(const Pa::Statement &stmt, std::string &, const Se::Symbol *&) { @@ -212,7 +217,7 @@ class FirConverter : public AbstractConverter { auto &n{std::get(stmt.statement.t)}; symbol = n.symbol; assert(symbol && "Name resolution failure"); - name = applyNameMangling(*symbol); // FIXME: use NameMangler + name = mangledName(*symbol); } void genFIR(const Pa::Statement &stmt, std::string &, const Se::Symbol *&) { @@ -257,17 +262,17 @@ class FirConverter : public AbstractConverter { builder->create(toLocation(), r); } void genFIR(const Pa::EndFunctionStmt &stmt) { - genFIRFunctionReturn(inFunction(currentEvaluation)); + genFIRFunctionReturn(static_cast(nullptr)); } template void genFIRProcedureExit(const A *) { // FIXME: alt-returns builder->create(toLocation()); } void genFIR(const Pa::EndSubroutineStmt &stmt) { - genFIRProcedureExit(inSubroutine(currentEvaluation)); + genFIRProcedureExit(static_cast(nullptr)); } void genFIR(const Pa::EndMpSubprogramStmt &stmt) { - genFIRProcedureExit(inMPSubp(currentEvaluation)); + genFIRProcedureExit(static_cast(nullptr)); } // @@ -474,7 +479,7 @@ class FirConverter : public AbstractConverter { assert(func.getType() == ty); return func; } - return createFunction(module, name, ty); + return createFunction(*this, name, ty); } /// Lowering of CALL statement @@ -844,7 +849,7 @@ class FirConverter : public AbstractConverter { } } auto funcTy{M::FunctionType::get(args, results, &mlirContext)}; - return createFunction(module, name, funcTy); + return createFunction(*this, name, funcTy); } /// Prepare to translate a new function @@ -901,10 +906,11 @@ class FirConverter : public AbstractConverter { assert((size == 1 || size == 2) && "ill-formed subprogram"); if (size == 2) { + currentEvaluation = nullptr; std::visit( [&](auto *p) { genFIR(*p, name, symbol); }, func.funStmts.front()); } else { - name = mangler.getProgramEntry(); + name = mangler.doProgramEntry(); } startNewFunction(func, name, symbol); @@ -913,6 +919,7 @@ class FirConverter : public AbstractConverter { for (auto &e : func.evals) { lowerEval(e); } + currentEvaluation = nullptr; std::visit( [&](auto *p) { genFIR(*p, name, symbol); }, func.funStmts.back()); @@ -983,9 +990,7 @@ class FirConverter : public AbstractConverter { // /// Convert a parser CharBlock to a Location - M::Location toLocation(const Pa::CharBlock &cb) { - return parserPosToLoc(mlirContext, cooked, cb); - } + M::Location toLocation(const Pa::CharBlock &cb) { return genLocation(cb); } M::Location toLocation() { return toLocation(currentPosition); } @@ -1034,7 +1039,7 @@ class FirConverter : public AbstractConverter { Pa::CharBlock currentPosition; CFGMapType cfgMap; std::list cfgEdgeSetPool; - AST::Evaluation *currentEvaluation; // FIXME: this is a hack + AST::Evaluation *currentEvaluation{nullptr}; // FIXME: this is a hack public: FirConverter() = delete; @@ -1095,12 +1100,24 @@ class FirConverter : public AbstractConverter { } M::Location getCurrentLocation() override { return toLocation(); } - M::Location genLocation() override { return dummyLoc(mlirContext); } + M::Location genLocation() override { + return M::UnknownLoc::get(&mlirContext); + } M::Location genLocation(const Pa::CharBlock &block) override { - return parserPosToLoc(mlirContext, cooked, block); + if (cooked) { + auto loc{cooked->GetSourcePositionRange(block)}; + if (loc.has_value()) { + // loc is a pair (begin, end); use the beginning position + auto &filePos{loc->first}; + return M::FileLineColLoc::get( + filePos.file.path(), filePos.line, filePos.column, &mlirContext); + } + } + return genLocation(); } M::OpBuilder &getOpBuilder() override { return *builder; } + M::ModuleOp &getModuleOp() override { return module; } }; } // namespace diff --git a/lib/burnside/bridge.h b/lib/burnside/bridge.h index 0ec81b7d569e..52fba728845d 100644 --- a/lib/burnside/bridge.h +++ b/lib/burnside/bridge.h @@ -99,6 +99,8 @@ class AbstractConverter { /// Get the OpBuilder virtual mlir::OpBuilder &getOpBuilder() = 0; + /// Get the ModuleOp + virtual mlir::ModuleOp &getModuleOp() = 0; virtual ~AbstractConverter() = default; }; diff --git a/lib/burnside/builder.cc b/lib/burnside/builder.cc index e768ad0ecec4..e064fb365641 100644 --- a/lib/burnside/builder.cc +++ b/lib/burnside/builder.cc @@ -20,17 +20,22 @@ #include "mlir/IR/Value.h" namespace B = Fortran::burnside; +namespace Ev = Fortran::evaluate; +namespace M = mlir; +namespace Se = Fortran::semantics; using namespace Fortran; -using namespace B; +using namespace Fortran::burnside; +#if 0 // This will need to be extended to consider the type of what is being mangled std::string B::applyNameMangling(llvm::StringRef parserName) { // FIXME: this is fake for now, add type info, etc. return "_Qp_"s + parserName.str(); } +#endif -std::string B::applyNameMangling(const evaluate::ProcedureDesignator &proc) { +std::string B::applyNameMangling(const Ev::ProcedureDesignator &proc) { if (const auto *symbol{proc.GetSymbol()}) { return applyNameMangling(*symbol); } else { @@ -41,7 +46,7 @@ std::string B::applyNameMangling(const evaluate::ProcedureDesignator &proc) { } } -std::string B::applyNameMangling(semantics::SymbolRef symbol) { +std::string B::applyNameMangling(Se::SymbolRef symbol) { // FIXME: this is fake for now, add type info, etc. // For now, only works for external procedures // TODO: apply binding @@ -50,14 +55,14 @@ std::string B::applyNameMangling(semantics::SymbolRef symbol) { // TODO: Apply proposed mangling with _Qp_ .... return std::visit( common::visitors{ - [&](const semantics::MainProgramDetails &) { return "MAIN_"s; }, - [&](const semantics::SubprogramDetails &) { + [&](const Se::MainProgramDetails &) { return "MAIN_"s; }, + [&](const Se::SubprogramDetails &) { return symbol->name().ToString() + "_"; }, - [&](const semantics::ProcEntityDetails &) { + [&](const Se::ProcEntityDetails &) { return symbol->name().ToString() + "_"; }, - [&](const semantics::SubprogramNameDetails &) { + [&](const Se::SubprogramNameDetails &) { assert(false && "SubprogramNameDetails not expected after semantic analysis"); return ""s; @@ -70,36 +75,39 @@ std::string B::applyNameMangling(semantics::SymbolRef symbol) { symbol->details()); } -mlir::FuncOp B::createFunction(mlir::ModuleOp module, llvm::StringRef name, - mlir::FunctionType funcTy, parser::CookedSource const *cooked, - parser::CharBlock const *cb) { - mlir::MLIRContext *ctxt{module.getContext()}; - mlir::Location loc{ - (cooked && cb) ? parserPosToLoc(*ctxt, cooked, *cb) : dummyLoc(*ctxt)}; - auto func{mlir::FuncOp::create(loc, name, funcTy)}; +M::FuncOp B::createFunction(B::AbstractConverter &converter, + llvm::StringRef name, M::FunctionType funcTy) { + auto func{M::FuncOp::create(converter.getCurrentLocation(), name, funcTy)}; + converter.getModuleOp().push_back(func); + return func; +} + +M::FuncOp B::createFunction( + M::ModuleOp module, llvm::StringRef name, M::FunctionType funcTy) { + auto loc{M::UnknownLoc::get(module.getContext())}; + auto func{M::FuncOp::create(loc, name, funcTy)}; module.push_back(func); return func; } -mlir::FuncOp B::getNamedFunction(mlir::ModuleOp module, llvm::StringRef name) { - return module.lookupSymbol(name); +M::FuncOp B::getNamedFunction(M::ModuleOp module, llvm::StringRef name) { + return module.lookupSymbol(name); } -void B::SymMap::addSymbol(semantics::SymbolRef symbol, mlir::Value *value) { +void B::SymMap::addSymbol(Se::SymbolRef symbol, M::Value *value) { symbolMap.try_emplace(&*symbol, value); } -mlir::Value *B::SymMap::lookupSymbol(semantics::SymbolRef symbol) { +M::Value *B::SymMap::lookupSymbol(Se::SymbolRef symbol) { auto iter{symbolMap.find(&*symbol)}; return (iter == symbolMap.end()) ? nullptr : iter->second; } -void B::SymMap::pushShadowSymbol( - semantics::SymbolRef symbol, mlir::Value *value) { +void B::SymMap::pushShadowSymbol(Se::SymbolRef symbol, M::Value *value) { // find any existing mapping for symbol auto iter{symbolMap.find(&*symbol)}; - const semantics::Symbol *sym{nullptr}; - mlir::Value *val{nullptr}; + const Se::Symbol *sym{nullptr}; + M::Value *val{nullptr}; // if mapping exists, save it on the shadow stack if (iter != symbolMap.end()) { sym = iter->first; diff --git a/lib/burnside/builder.h b/lib/burnside/builder.h index 9ac477e8211a..02eef8144b89 100644 --- a/lib/burnside/builder.h +++ b/lib/burnside/builder.h @@ -41,6 +41,8 @@ namespace burnside { /// /// [Coding style](https://llvm.org/docs/CodingStandards.html) +class AbstractConverter; + class SymMap { llvm::DenseMap symbolMap; std::vector> shadowStack; @@ -59,7 +61,6 @@ class SymMap { } }; -std::string applyNameMangling(llvm::StringRef parserName); std::string applyNameMangling(const evaluate::ProcedureDesignator &proc); std::string applyNameMangling(semantics::SymbolRef symbol); @@ -90,12 +91,14 @@ inline mlir::Block *createBlock(mlir::OpBuilder *bldr) { /// Get a function by name (or null) mlir::FuncOp getNamedFunction(mlir::ModuleOp, llvm::StringRef name); -/// Create a new Function -/// Both the `CookedSource` and `CharBlock` position should be provided to -/// properly track source position information. -mlir::FuncOp createFunction(mlir::ModuleOp module, llvm::StringRef name, - mlir::FunctionType funcTy, parser::CookedSource const *cooked = nullptr, - parser::CharBlock const *cb = nullptr); +/// Create a new FuncOp +mlir::FuncOp createFunction(AbstractConverter &converter, llvm::StringRef name, + mlir::FunctionType funcTy); + +/// Create a new FuncOp +/// The function is created with no Location information +mlir::FuncOp createFunction( + mlir::ModuleOp module, llvm::StringRef name, mlir::FunctionType funcTy); } // burnside } // Fortran diff --git a/lib/burnside/convert-type.cc b/lib/burnside/convert-type.cc index 1b5cbe5575b3..81c53a2e70da 100644 --- a/lib/burnside/convert-type.cc +++ b/lib/burnside/convert-type.cc @@ -338,24 +338,6 @@ class TypeBuilder { } // namespace -M::Location Br::dummyLoc(M::MLIRContext &context) { - return M::UnknownLoc::get(&context); -} - -M::Location Br::parserPosToLoc(M::MLIRContext &context, - const Pa::CookedSource *cooked, const Pa::CharBlock &position) { - if (cooked) { - auto loc{cooked->GetSourcePositionRange(position)}; - if (loc.has_value()) { - // loc is a pair (begin, end); use the beginning position - auto &filePos{loc->first}; - return M::FileLineColLoc::get( - filePos.file.path(), filePos.line, filePos.column, &context); - } - } - return dummyLoc(context); -} - M::Type Br::getFIRType(M::MLIRContext *context, Co::IntrinsicTypeDefaultKinds const &defaults, Co::TypeCategory tc, int kind) { @@ -381,7 +363,7 @@ M::Type Br::translateSomeExprToFIRType(M::MLIRContext *context, // This entry point avoids gratuitously wrapping the Symbol instance in layers // of Expr that will then be immediately peeled back off and discarded. M::Type Br::translateSymbolToFIRType(M::MLIRContext *context, - Co::IntrinsicTypeDefaultKinds const &defaults, Se::SymbolRef symbol) { + Co::IntrinsicTypeDefaultKinds const &defaults, const SymbolRef symbol) { return TypeBuilder{context, defaults}.gen(symbol); } diff --git a/lib/burnside/convert-type.h b/lib/burnside/convert-type.h index 05e570082a96..658340396c82 100644 --- a/lib/burnside/convert-type.h +++ b/lib/burnside/convert-type.h @@ -35,7 +35,7 @@ class Type; namespace Fortran { namespace common { class IntrinsicTypeDefaultKinds; -template class Reference; +template class Reference; } // common namespace evaluate { @@ -54,12 +54,12 @@ class CookedSource; namespace semantics { class Symbol; -using SymbolRef = common::Reference; } // semantics namespace burnside { using SomeExpr = evaluate::Expr; +using SymbolRef = common::Reference; constexpr common::TypeCategory IntegerCat{common::TypeCategory::Integer}; constexpr common::TypeCategory RealCat{common::TypeCategory::Real}; @@ -68,14 +68,6 @@ constexpr common::TypeCategory CharacterCat{common::TypeCategory::Character}; constexpr common::TypeCategory LogicalCat{common::TypeCategory::Logical}; constexpr common::TypeCategory DerivedCat{common::TypeCategory::Derived}; -/// Generate a dummy location when there is no origin -mlir::Location dummyLoc(mlir::MLIRContext &context); - -/// Convert a `CharBlock` front-end position pointer into the `(file, line, -/// column)` triple for use in MLIR, LLVM, and ultimately DWARF. -mlir::Location parserPosToLoc(mlir::MLIRContext &context, - parser::CookedSource const *cooked, parser::CharBlock const &position); - mlir::Type getFIRType(mlir::MLIRContext *ctxt, common::IntrinsicTypeDefaultKinds const &defaults, common::TypeCategory tc, int kind); @@ -104,8 +96,7 @@ mlir::Type translateSomeExprToFIRType(mlir::MLIRContext *ctxt, common::IntrinsicTypeDefaultKinds const &defaults, const SomeExpr *expr); mlir::Type translateSymbolToFIRType(mlir::MLIRContext *ctxt, - common::IntrinsicTypeDefaultKinds const &defaults, - const semantics::SymbolRef symbol); + common::IntrinsicTypeDefaultKinds const &defaults, const SymbolRef symbol); mlir::Type convertReal(mlir::MLIRContext *ctxt, int KIND); diff --git a/lib/burnside/mangler.cc b/lib/burnside/mangler.cc new file mode 100644 index 000000000000..ee05cc836dd2 --- /dev/null +++ b/lib/burnside/mangler.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "mangler.h" +#include "../common/reference.h" +#include "../semantics/tools.h" +#include "fir/InternalNames.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" + +namespace Br = Fortran::burnside; +namespace Co = Fortran::common; +namespace L = llvm; +namespace Ma = Fortran::burnside::mangle; +namespace Se = Fortran::semantics; + +using namespace Fortran; + +namespace { + +L::StringRef toStringRef(const parser::CharBlock &cb) { + return L::StringRef{cb.begin(), cb.size()}; +} + +// recursively build the vector of module scopes +void moduleNames( + const Se::Scope *scope, L::SmallVector &result) { + if (scope->kind() == Se::Scope::Kind::Global) { + return; + } + moduleNames(&scope->parent(), result); + if (scope->kind() == Se::Scope::Kind::Module) + if (auto *symbol = scope->symbol()) + result.emplace_back(toStringRef(symbol->name())); +} + +L::SmallVector moduleNames(const Se::Scope *scope) { + L::SmallVector result; + moduleNames(scope, result); + return result; +} + +L::Optional hostName(const Se::Scope *scope) { + if (scope->kind() == Se::Scope::Kind::Subprogram) { + auto &parent = scope->parent(); + if (parent.kind() == Se::Scope::Kind::Subprogram) + if (auto *symbol = parent.symbol()) { + return {toStringRef(symbol->name())}; + } + } + return {}; +} + +} // namespace + +// Mangle the name of `symbol` to make it unique within FIR's symbol table using +// the FIR name mangler, `mangler` +std::string Ma::mangleName( + fir::NameMangler &mangler, const Se::SymbolRef symbol) { + return std::visit(Co::visitors{ + [&](const Se::MainProgramDetails &) { + return mangler.doProgramEntry().str(); + }, + [&](const Se::SubprogramDetails &) { + auto &cb{symbol->name()}; + auto modNames{moduleNames(symbol->scope())}; + return mangler.doProcedure(modNames, + hostName(symbol->scope()), toStringRef(cb)); + }, + [&](const Se::ProcEntityDetails &) { + auto &cb{symbol->name()}; + auto modNames{moduleNames(symbol->scope())}; + return mangler.doProcedure(modNames, + hostName(symbol->scope()), toStringRef(cb)); + }, + [](const auto &) -> std::string { + assert(false); + return {}; + }, + }, + symbol->details()); +} + +std::string Ma::demangleName(L::StringRef name) { + return name.str(); +} diff --git a/lib/burnside/mangler.h b/lib/burnside/mangler.h new file mode 100644 index 000000000000..145640ac5135 --- /dev/null +++ b/lib/burnside/mangler.h @@ -0,0 +1,51 @@ +// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FORTRAN_BURNSIDE_MANGLER_H_ +#define FORTRAN_BURNSIDE_MANGLER_H_ + +#include + +namespace fir { +struct NameMangler; +} + +namespace llvm { +class StringRef; +} + +namespace Fortran { +namespace common { +template class Reference; +} + +namespace semantics { +class Symbol; +} + +namespace burnside { +using SymbolRef = common::Reference; + +namespace mangle { + +/// Convert a front-end Symbol to an internal name +std::string mangleName(fir::NameMangler &mangler, const SymbolRef symbol); + +std::string demangleName(llvm::StringRef name); + +} // mangle +} // burnside +} // Fortran + +#endif // FORTRAN_BURNSIDE_MANGLER_H_ diff --git a/lib/fir/InternalNames.cpp b/lib/fir/InternalNames.cpp index 3f7657a462ba..b9a47cfd24b0 100644 --- a/lib/fir/InternalNames.cpp +++ b/lib/fir/InternalNames.cpp @@ -22,74 +22,84 @@ namespace L = llvm; namespace { -inline L::Twine prefix() { return "_Q"; } +inline L::StringRef prefix() { return "_Q"; } -L::Twine doModules(L::ArrayRef mods) { - L::Twine result; +std::string doModules(L::ArrayRef mods) { + std::string result; auto *token = "M"; for (auto mod : mods) { - result.concat(token).concat(mod); + L::Twine t = result + token + mod; + result = t.str(); token = "S"; } return result; } -L::Twine doModulesHost(L::ArrayRef mods, - L::Optional host) { - L::Twine result = doModules(mods); - if (host.hasValue()) - result.concat("F").concat(*host); +std::string doModulesHost(L::ArrayRef mods, + L::Optional host) { + auto result = doModules(mods); + if (host.hasValue()) { + L::Twine t = result + "F" + *host; + result = t.str(); + } return result; } } // namespace -L::Twine fir::NameMangler::toLower(L::StringRef name) { +L::StringRef fir::NameMangler::toLower(L::StringRef name) { auto lo = name.lower(); if (name.equals(lo)) return name; return cache.insert(lo).first->getKey(); } -L::Twine fir::NameMangler::addAsString(std::int64_t i) { +L::StringRef fir::NameMangler::addAsString(std::int64_t i) { return cache.insert(std::to_string(i)).first->getKey(); } -L::Twine fir::NameMangler::doKind(std::int64_t kind) { - if (kind < 0) - return "KN" + addAsString(-kind); - return "K" + addAsString(kind); +std::string fir::NameMangler::doKind(std::int64_t kind) { + if (kind < 0) { + L::Twine result = "KN" + addAsString(-kind); + return result.str(); + } + L::Twine result = "K" + addAsString(kind); + return result.str(); } -L::Twine fir::NameMangler::doKinds(L::ArrayRef kinds) { +std::string fir::NameMangler::doKinds(L::ArrayRef kinds) { L::Twine result; for (auto i : kinds) result.concat(doKind(i)); - return result; + return result.str(); } -L::Twine fir::NameMangler::doCommonBlock(L::StringRef name) { - return prefix() + "B" + toLower(name); +std::string fir::NameMangler::doCommonBlock(L::StringRef name) { + L::Twine result = prefix() + "B" + toLower(name); + return result.str(); } -L::Twine fir::NameMangler::doConstant(L::ArrayRef modules, - L::StringRef name) { - return prefix() + doModules(modules) + "EC" + toLower(name); +std::string fir::NameMangler::doConstant(L::ArrayRef modules, + L::StringRef name) { + L::Twine result = prefix() + doModules(modules) + "EC" + toLower(name); + return result.str(); } -L::Twine fir::NameMangler::doDispatchTable(L::ArrayRef modules, - L::Optional host, - L::StringRef name, - L::ArrayRef kinds) { - return prefix() + doModulesHost(modules, host) + "DT" + toLower(name) + - doKinds(kinds); +std::string fir::NameMangler::doDispatchTable(L::ArrayRef modules, + L::Optional host, + L::StringRef name, + L::ArrayRef kinds) { + L::Twine result = prefix() + doModulesHost(modules, host) + "DT" + + toLower(name) + doKinds(kinds); + return result.str(); } -L::Twine fir::NameMangler::doGenerated(L::StringRef name) { - return prefix() + "Q" + toLower(name); +std::string fir::NameMangler::doGenerated(L::StringRef name) { + L::Twine result = prefix() + "Q" + toLower(name); + return result.str(); } -L::Twine fir::NameMangler::doIntrinsicTypeDescriptor( +std::string fir::NameMangler::doIntrinsicTypeDescriptor( L::ArrayRef modules, L::Optional host, IntrinsicType type, std::int64_t kind) { char const *name; @@ -110,32 +120,38 @@ L::Twine fir::NameMangler::doIntrinsicTypeDescriptor( name = "real"; break; } - return prefix() + doModulesHost(modules, host) + "C" + name + doKind(kind); + L::Twine result = + prefix() + doModulesHost(modules, host) + "C" + name + doKind(kind); + return result.str(); } -L::Twine fir::NameMangler::doProcedure(L::ArrayRef modules, - L::Optional host, - L::StringRef name) { - return prefix() + doModulesHost(modules, host) + "P" + toLower(name); +std::string fir::NameMangler::doProcedure(L::ArrayRef modules, + L::Optional host, + L::StringRef name) { + L::Twine result = + prefix() + doModulesHost(modules, host) + "P" + toLower(name); + return result.str(); } -L::Twine fir::NameMangler::doType(L::ArrayRef modules, - L::Optional host, - L::StringRef name, - L::ArrayRef kinds) { - return prefix() + doModulesHost(modules, host) + "T" + toLower(name) + - doKinds(kinds); +std::string fir::NameMangler::doType(L::ArrayRef modules, + L::Optional host, + L::StringRef name, + L::ArrayRef kinds) { + L::Twine result = prefix() + doModulesHost(modules, host) + "T" + + toLower(name) + doKinds(kinds); + return result.str(); } -L::Twine fir::NameMangler::doTypeDescriptor(L::ArrayRef modules, - L::Optional host, - L::StringRef name, - L::ArrayRef kinds) { - return prefix() + doModulesHost(modules, host) + "CT" + toLower(name) + - doKinds(kinds); +std::string fir::NameMangler::doTypeDescriptor( + L::ArrayRef modules, L::Optional host, + L::StringRef name, L::ArrayRef kinds) { + L::Twine result = prefix() + doModulesHost(modules, host) + "CT" + + toLower(name) + doKinds(kinds); + return result.str(); } -L::Twine fir::NameMangler::doVariable(L::ArrayRef modules, - L::StringRef name) { - return prefix() + doModules(modules) + "E" + toLower(name); +std::string fir::NameMangler::doVariable(L::ArrayRef modules, + L::StringRef name) { + L::Twine result = prefix() + doModules(modules) + "E" + toLower(name); + return result.str(); } From f67a92f5ec1e71193b2511ae54e8960a825c3298 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Wed, 11 Dec 2019 05:52:58 -0800 Subject: [PATCH 066/123] Fix function returns and enable more expression tests - fix function distance to support fir.real vs mlir.float - fix function returns when no explicit parser::ReturnStmt - enable expression lowering tests with complex --- lib/burnside/bridge.cc | 30 ++++++++------- lib/burnside/intrinsics.cc | 55 +++++++++++++++++----------- test/burnside/expr-test-generator.cc | 33 +++++++++++------ 3 files changed, 70 insertions(+), 48 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 5607189dc746..67109fb813ba 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -94,6 +94,10 @@ class FirConverter : public AbstractConverter { return createI1LogicalExpression( loc, *builder, *expr, localSymbols, defaults, intrinsics); } + M::Value *createTemporary(M::Location loc, const Se::Symbol &sym) { + return ::createTemporary(loc, *builder, localSymbols, + translateSymbolToFIRType(builder->getContext(), defaults, sym), &sym); + } std::string mangledName(SymbolRef symbol) { return mangle::mangleName(mangler, symbol); @@ -207,9 +211,10 @@ class FirConverter : public AbstractConverter { name = mangledName(*symbol); } void genFIR(const Pa::Statement &stmt, std::string &, - const Se::Symbol *&) { + const Se::Symbol *&symbol) { setCurrentPosition(stmt.source); - genFIR(stmt.statement); + assert(symbol); + genFIRFunctionReturn(*symbol); } void genFIR(const Pa::Statement &stmt, std::string &name, const Se::Symbol *&symbol) { @@ -250,20 +255,12 @@ class FirConverter : public AbstractConverter { /// END of procedure-like constructs /// /// Generate the cleanup block before the procedure exits - void genFIRFunctionReturn(const Pa::FunctionStmt *stmt) { - auto &name = std::get(stmt->t); - assert(name.symbol); - const auto &details{name.symbol->get()}; + void genFIRFunctionReturn(const Se::Symbol &functionSymbol) { + const auto &details{functionSymbol.get()}; M::Value *resultRef{localSymbols.lookupSymbol(details.result())}; - // FIXME: what happens if result was never referenced before and hence no - // temp was created? - assert(resultRef); M::Value *r{builder->create(toLocation(), resultRef)}; builder->create(toLocation(), r); } - void genFIR(const Pa::EndFunctionStmt &stmt) { - genFIRFunctionReturn(static_cast(nullptr)); - } template void genFIRProcedureExit(const A *) { // FIXME: alt-returns builder->create(toLocation()); @@ -775,13 +772,15 @@ class FirConverter : public AbstractConverter { } // gen expression, if any; share code with END of procedure - void genFIR(const Pa::ReturnStmt &stmt) { + void genFIR(const Pa::ReturnStmt &) { if (inMainProgram(currentEvaluation)) { builder->create(toLocation()); } else if (auto *stmt = inSubroutine(currentEvaluation)) { genFIRProcedureExit(stmt); } else if (auto *stmt = inFunction(currentEvaluation)) { - genFIRFunctionReturn(stmt); + auto *symbol = std::get(stmt->t).symbol; + assert(symbol); + genFIRFunctionReturn(*symbol); } else if (auto *stmt = inMPSubp(currentEvaluation)) { genFIRProcedureExit(stmt); } else { @@ -878,6 +877,9 @@ class FirConverter : public AbstractConverter { TODO(); // handle alternate return } } + if (details->isFunction()) { + createTemporary(toLocation(), details->result()); + } } } diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index 5f7487d20183..753d30b7e7f8 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -278,10 +278,15 @@ mlir::FuncOp MathRuntimeLibrary::getFuncOp( } // This helper class computes a "distance" between two function types. -// The distance represents the argument and result conversions -// that are required if one use "to" instead of "from". -// Note that this is not a reflexive distance, and it does not -// define a total order at all. +// The distance measures how many narrowing conversions of actual arguments +// and result of "from" must be made in order to use "to" instead of "from". +// For instance, the distance between ACOS(REAL(10)) and ACOS(REAL(8)) is +// greater than the one between ACOS(REAL(10)) and ACOS(REAL(16)). This means +// if no implementation of ACOS(REAL(10)) is available, it is better to use +// ACOS(REAL(16)) with casts rather than ACOS(REAL(8)). +// Note that this is not a symmetric distance and the order of "from" and "to" +// arguments matters, d(foo, bar) may not be the same as d(bar, foo) because it +// may be safe to replace foo by bar, but not the opposite. class FunctionDistance { public: FunctionDistance() : infinite{true} {} @@ -329,7 +334,20 @@ class FunctionDistance { case Conversion::Extend: conversions[extendingResult]++; break; } } - + // Floating point can be mlir::FloatType or fir::real + static unsigned getFloatingPointWidth(mlir::Type t) { + if (auto f{t.dyn_cast()}) return f.getWidth(); + // FIXME: Get width another way for fir.real/complex + // - use fir/KindMapping.h and LLVM::Type + // - or use evaluate/type.h + if (auto r{t.dyn_cast()}) return r.getFKind() * 4; + if (auto cplx{t.dyn_cast()}) return cplx.getFKind() * 4; + assert(false && "not a floating-point type"); + return 0; + } + static bool isFloatingPointType(mlir::Type t) { + return t.isa() || t.isa(); + } static Conversion conversionBetweenTypes(mlir::Type from, mlir::Type to) { if (from == to) { return Conversion::None; @@ -340,24 +358,17 @@ class FunctionDistance { : Conversion::Extend; } } - if (auto fromRealTy{from.dyn_cast()}) { - if (auto toRealTy{to.dyn_cast()}) { - // FIXME: With the current runtime (no functions with f16 and bf16 - // types), and the current kinds to representation mapping, that - // just works, but that is not that simple: - // - conversions between f16 and bf16 destroy information in both - // ways. - // - No assumptions regarding kind <-> representation should be made - // here. - return fromRealTy.getFKind() > toRealTy.getFKind() ? Conversion::Narrow - : Conversion::Extend; - } + if (isFloatingPointType(from) && isFloatingPointType(to)) { + return getFloatingPointWidth(from) > getFloatingPointWidth(to) + ? Conversion::Narrow + : Conversion::Extend; } if (auto fromCplxTy{from.dyn_cast()}) { - if (auto toCplxTy{to.dyn_cast()}) { - // FIXME: Same as for Real. - return fromCplxTy.getFKind() > toCplxTy.getFKind() ? Conversion::Narrow - : Conversion::Extend; + if (auto toCplxTy{to.dyn_cast()}) { + return getFloatingPointWidth(fromCplxTy) > + getFloatingPointWidth(toCplxTy) + ? Conversion::Narrow + : Conversion::Extend; } } // Notes: @@ -554,7 +565,7 @@ mlir::Value *IntrinsicLibrary::Implementation::genRuntimeCall( // TODO: better error handling ? // - Try to have compile time check of runtime compltness ? } - return {}; // gets rid of warnings + return {}; // gets rid of warnings } // CONJG diff --git a/test/burnside/expr-test-generator.cc b/test/burnside/expr-test-generator.cc index 96e6fad39298..fa246d14efff 100644 --- a/test/burnside/expr-test-generator.cc +++ b/test/burnside/expr-test-generator.cc @@ -522,15 +522,25 @@ struct CodeGenerator { } void GenerateResultsComparison(std::ostream &s) const { - const auto *compareOp{ - resultType.cat == TypeCategory::Logical ? ".EQV." : ".EQ."}; - if (resultType.cat == TypeCategory::Real) { + auto compareReal{[&](const std::string &x, const std::string &y) { // TODO: This should not always be an absolute comparison (epsilon margin - // for fp.). Complex needs similar checks. - s << refResultName << compareOp << testResultName << " .OR. "; - s << "(IEEE_IS_NAN(" << refResultName << ") .AND. IEEE_IS_NAN(" - << testResultName << "))"; + // for fp.). + s << x << ".EQ." << y << " .OR. "; + s << "(IEEE_IS_NAN(" << x << ") .AND. IEEE_IS_NAN(" << y << "))"; + }}; + if (resultType.cat == TypeCategory::Real) { + compareReal(refResultName, testResultName); + } else if (resultType.cat == TypeCategory::Complex) { + s << "("; + compareReal(std::string{refResultName} + "%RE", + std::string{testResultName} + "%RE"); + s << ") .AND. ("; + compareReal(std::string{refResultName} + "%IM", + std::string{testResultName} + "%IM"); + s << ")"; } else { + const auto *compareOp{ + resultType.cat == TypeCategory::Logical ? ".EQV." : ".EQ."}; s << refResultName << compareOp << testResultName; } } @@ -675,11 +685,10 @@ int main(int argc, char **argv) { {Integer8, {"3000001_8", "-2654637545_8"}}, {Real4, {"1.03687448_4", "3.1254641_4"}}, {Real8, {"1.036874168446448_8", "3.12254533554641_8"}}, - // TODO: fix expression analysis bug with REAL(8)**COMPLEX(4) - // {Complex4, {"(-0.5_4,10.35544_4)", "(-5._4, 0.15647_4)"}}, - // {Complex8, - // {"(-0.5_8,10.35546579874_8)", - // "(-5.64654654_8, 0.155876974647_8)"}}, + {Complex4, {"(-0.5_4,10.35544_4)", "(-5._4, 0.15647_4)"}}, + {Complex8, + {"(-0.5_8,10.35546579874_8)", + "(-5.64654654_8, 0.155876974647_8)"}}, {DefaultLogical, {".false.", ".true."}}, }, refEvalMethod, From 2d93463ccc7735dfacc5f43e46696dbd6346bef9 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 12 Dec 2019 10:19:34 -0800 Subject: [PATCH 067/123] add a decompose interface change "mangle" to "unique" more threading of abstract converter library name change make the asserts have a command line control use -disable-burnside-todo to disable the assertions --- include/fir/InternalNames.h | 61 ++++++++++++----- include/fir/Tilikum/Tilikum.h | 4 +- lib/burnside/bridge.cc | 120 +++++++++++++++++++--------------- lib/burnside/bridge.h | 13 +++- lib/burnside/builder.cc | 48 -------------- lib/burnside/builder.h | 3 - lib/burnside/cfg-builder.h | 48 +++++++------- lib/burnside/convert-expr.cc | 83 ++++++++++++----------- lib/burnside/convert-expr.h | 22 +++---- lib/burnside/convert-type.cc | 3 +- lib/burnside/mangler.cc | 11 ++-- lib/burnside/mangler.h | 4 +- lib/fir/InternalNames.cpp | 37 ++++++----- lib/fir/Tilikum.cpp | 18 ++--- tools/bbc/bbc.cpp | 6 +- tools/tco/CMakeLists.txt | 2 +- tools/tco/tco.cpp | 4 +- 17 files changed, 246 insertions(+), 241 deletions(-) diff --git a/include/fir/InternalNames.h b/include/fir/InternalNames.h index 49637e26d47c..78c58af996b1 100644 --- a/include/fir/InternalNames.h +++ b/include/fir/InternalNames.h @@ -29,56 +29,87 @@ class Twine; namespace fir { /// Internal name mangling of identifiers -struct NameMangler { +/// +/// In order to generate symbolically referencable artifacts in a ModuleOp, +/// it is required that those symbols be uniqued. This is a simple interface +/// for converting Fortran symbols into unique names. +/// +/// This is intentionally bijective. Given a symbol's parse name, type, and +/// scope-like information, we can generate a uniqued (mangled) name. Given a +/// uniqued name, we can return the symbol parse name, type of the symbol, and +/// any scope-like information for that symbol. +struct NameUniquer { enum class IntrinsicType { CHARACTER, COMPLEX, INTEGER, LOGICAL, REAL }; - NameMangler() = default; - - /// Mangle a common block name + enum class NameKind { + NOT_UNIQUED, + COMMON, + CONSTANT, + DERIVED_TYPE, + DISPATCH_TABLE, + GENERATED, + INTRINSIC_TYPE_DESC, + PROCEDURE, + TYPE_DESC, + VARIABLE + }; + + struct DeconstructedName { + llvm::SmallVector modules; + llvm::Optional host; + std::string name; + llvm::SmallVector kinds; + }; + + NameUniquer() = default; + + /// Unique a common block name std::string doCommonBlock(llvm::StringRef name); - /// Mangle a (global) constant name + /// Unique a (global) constant name std::string doConstant(llvm::ArrayRef modules, llvm::StringRef name); - /// Mangle a dispatch table name + /// Unique a dispatch table name std::string doDispatchTable(llvm::ArrayRef modules, llvm::Optional host, llvm::StringRef name, llvm::ArrayRef kinds); - /// Mangle a compiler generated name + /// Unique a compiler generated name std::string doGenerated(llvm::StringRef name); - /// Mangle an intrinsic type descriptor + /// Unique an intrinsic type descriptor std::string doIntrinsicTypeDescriptor(llvm::ArrayRef modules, llvm::Optional host, IntrinsicType type, std::int64_t kind); - /// Mangle a procedure name + /// Unique a procedure name std::string doProcedure(llvm::ArrayRef modules, llvm::Optional host, llvm::StringRef name); - /// Mangle a derived type name + /// Unique a derived type name std::string doType(llvm::ArrayRef modules, llvm::Optional host, llvm::StringRef name, llvm::ArrayRef kinds); - /// Mangle a (derived) type descriptor name + /// Unique a (derived) type descriptor name std::string doTypeDescriptor(llvm::ArrayRef modules, llvm::Optional host, llvm::StringRef name, llvm::ArrayRef kinds); - /// Mangle a (global) variable name + /// Unique a (global) variable name std::string doVariable(llvm::ArrayRef modules, llvm::StringRef name); /// Entry point for the PROGRAM (called by the runtime) - constexpr static llvm::StringRef doProgramEntry() { - return "MAIN_"; - } + constexpr static llvm::StringRef doProgramEntry() { return "MAIN_"; } + + /// Decompose `uniquedName` into the parse name, symbol type, and scope info + static std::pair + deconstruct(llvm::StringRef uniquedName); private: llvm::StringRef addAsString(std::int64_t i); diff --git a/include/fir/Tilikum/Tilikum.h b/include/fir/Tilikum/Tilikum.h index a2c8571fdaf5..691e56f00d13 100644 --- a/include/fir/Tilikum/Tilikum.h +++ b/include/fir/Tilikum/Tilikum.h @@ -26,10 +26,10 @@ class Pass; namespace fir { -struct NameMangler; +struct NameUniquer; /// Convert FIR to the LLVM IR dialect -std::unique_ptr createFIRToLLVMPass(NameMangler &mangler); +std::unique_ptr createFIRToLLVMPass(NameUniquer &uniquer); /// Convert the LLVM IR dialect to LLVM-IR proper std::unique_ptr diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 67109fb813ba..53ef8b7a2518 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -27,21 +27,13 @@ #include "fir/FIROps.h" #include "fir/FIRType.h" #include "fir/InternalNames.h" +#include "llvm/Support/CommandLine.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/StandardOps/Ops.h" #include "mlir/IR/Builders.h" #include "mlir/Parser.h" #include "mlir/Target/LLVMIR.h" -#undef TODO -#define TODO() \ - llvm::errs() << __FILE__ << ":" << __LINE__ << " not yet implemented\n"; \ - std::exit(1) - -#undef SOFT_TODO -#define SOFT_TODO() \ - llvm::errs() << __FILE__ << ":" << __LINE__ << " not yet implemented\n"; - namespace Br = Fortran::burnside; namespace Co = Fortran::common; namespace Ev = Fortran::evaluate; @@ -55,6 +47,14 @@ using namespace Fortran::burnside; namespace { +L::cl::opt ClDisableToDoAssert("disable-burnside-todo", + L::cl::desc("disable burnside bridge asserts"), L::cl::init(false), + L::cl::Hidden); + +#undef TODO +#define TODO() \ + assert(false && "not implemented yet") + using SelectCaseConstruct = Pa::CaseConstruct; using SelectRankConstruct = Pa::SelectRankConstruct; using SelectTypeConstruct = Pa::SelectTypeConstruct; @@ -69,6 +69,16 @@ constexpr static bool isStopStmt(const Pa::StopStmt &stm) { // CfgBuilder implementation #include "cfg-builder.h" +#undef TODO +#define TODO() \ + { \ + if (ClDisableToDoAssert) \ + mlir::emitError(toLocation(), __FILE__) \ + << ":" << __LINE__ << " not implemented"; \ + else \ + assert(false && "not yet implemented"); \ + } + /// Converter from AST to FIR /// /// After building the AST and decorating it, the FirConverter processes that @@ -82,25 +92,18 @@ class FirConverter : public AbstractConverter { // M::Value *createFIRAddr(M::Location loc, const Se::SomeExpr *expr) { - return createSomeAddress( - loc, *builder, *expr, localSymbols, defaults, intrinsics); + return createSomeAddress(loc, *this, *expr, localSymbols, intrinsics); } M::Value *createFIRExpr(M::Location loc, const Se::SomeExpr *expr) { - return createSomeExpression( - loc, *builder, *expr, localSymbols, defaults, intrinsics); + return createSomeExpression(loc, *this, *expr, localSymbols, intrinsics); } M::Value *createLogicalExprAsI1(M::Location loc, const Se::SomeExpr *expr) { return createI1LogicalExpression( - loc, *builder, *expr, localSymbols, defaults, intrinsics); + loc, *this, *expr, localSymbols, intrinsics); } M::Value *createTemporary(M::Location loc, const Se::Symbol &sym) { - return ::createTemporary(loc, *builder, localSymbols, - translateSymbolToFIRType(builder->getContext(), defaults, sym), &sym); - } - - std::string mangledName(SymbolRef symbol) { - return mangle::mangleName(mangler, symbol); + return Br::createTemporary(loc, *builder, localSymbols, genType(sym), &sym); } // TODO: we need a map for the various Fortran runtime entry points @@ -195,7 +198,7 @@ class FirConverter : public AbstractConverter { void genFIR(const Pa::Statement &stmt, std::string &name, const Se::Symbol *&) { setCurrentPosition(stmt.source); - name = mangler.doProgramEntry(); + name = uniquer.doProgramEntry(); } void genFIR(const Pa::Statement &stmt, std::string &, const Se::Symbol *&) { @@ -208,7 +211,7 @@ class FirConverter : public AbstractConverter { auto &n{std::get(stmt.statement.t)}; symbol = n.symbol; assert(symbol && "Name resolution failure"); - name = mangledName(*symbol); + name = mangleName(*symbol); } void genFIR(const Pa::Statement &stmt, std::string &, const Se::Symbol *&symbol) { @@ -222,7 +225,7 @@ class FirConverter : public AbstractConverter { auto &n{std::get(stmt.statement.t)}; symbol = n.symbol; assert(symbol && "Name resolution failure"); - name = mangledName(*symbol); + name = mangleName(*symbol); } void genFIR(const Pa::Statement &stmt, std::string &, const Se::Symbol *&) { @@ -265,10 +268,10 @@ class FirConverter : public AbstractConverter { // FIXME: alt-returns builder->create(toLocation()); } - void genFIR(const Pa::EndSubroutineStmt &stmt) { + void genFIR(const Pa::EndSubroutineStmt &) { genFIRProcedureExit(static_cast(nullptr)); } - void genFIR(const Pa::EndMpSubprogramStmt &stmt) { + void genFIR(const Pa::EndMpSubprogramStmt &) { genFIRProcedureExit(static_cast(nullptr)); } @@ -339,8 +342,8 @@ class FirConverter : public AbstractConverter { void genFIRIOSwitch(AST::Evaluation &) { TODO(); } // Iterative loop control-flow semantics - void genFIREvalIterative(AST::Evaluation &eval) { - // FIXME + void genFIREvalIterative(AST::Evaluation &) { + TODO(); } void switchInsertionPointToWhere(fir::WhereOp &where) { @@ -500,7 +503,7 @@ class FirConverter : public AbstractConverter { // TODO bindName()? argsList = details.dummyArgs(); }, - [](const Pa::ProcComponentRef &) { TODO(); }, + [&](const Pa::ProcComponentRef &) { TODO(); }, }, std::get(stmt.v.t).u); for (auto *d : argsList) { @@ -511,6 +514,7 @@ class FirConverter : public AbstractConverter { auto funTy{M::FunctionType::get(argTy, resTy, builder->getContext())}; // FIXME: mangle name M::FuncOp func{getFunc(funName, funTy)}; + (void)func; // FIXME std::vector actuals; for (auto &aa : std::get>(stmt.v.t)) { auto &kw = std::get>(aa.t); @@ -521,9 +525,9 @@ class FirConverter : public AbstractConverter { // FIXME: needs to match argument, assumes trivial by-ref fe = genExprAddr(*Se::GetExpr(e)); }, - [](const Pa::AltReturnSpec &) { TODO(); }, - [](const Pa::ActualArg::PercentRef &) { TODO(); }, - [](const Pa::ActualArg::PercentVal &) { TODO(); }, + [&](const Pa::AltReturnSpec &) { TODO(); }, + [&](const Pa::ActualArg::PercentRef &) { TODO(); }, + [&](const Pa::ActualArg::PercentVal &) { TODO(); }, }, arg.u); if (kw.has_value()) { @@ -572,9 +576,9 @@ class FirConverter : public AbstractConverter { // pushDoContext(&ss); } #endif - SOFT_TODO(); + TODO(); } - void genFIR(const Pa::IfConstruct &) { SOFT_TODO(); } + void genFIR(const Pa::IfConstruct &) { TODO(); } void genFIR(const SelectCaseConstruct &) { TODO(); } void genFIR(const SelectRankConstruct &) { TODO(); } @@ -673,7 +677,7 @@ class FirConverter : public AbstractConverter { void genFIR(const Pa::RewindStmt &stmt) { genRewindStatement(*this, stmt); } void genFIR(const Pa::WriteStmt &stmt) { genWriteStatement(*this, stmt); } - void genFIR(const Pa::AllocateStmt &) { SOFT_TODO(); } + void genFIR(const Pa::AllocateStmt &) { TODO(); } void genFIR(const Pa::AssignmentStmt &stmt) { auto *rhs{Se::GetExpr(std::get(stmt.t))}; auto *lhs{Se::GetExpr(std::get(stmt.t))}; @@ -682,7 +686,7 @@ class FirConverter : public AbstractConverter { } void genFIR(const Pa::ContinueStmt &) {} // do nothing - void genFIR(const Pa::DeallocateStmt &) { SOFT_TODO(); } + void genFIR(const Pa::DeallocateStmt &) { TODO(); } void genFIR(const Pa::EventPostStmt &) { // call some runtime routine TODO(); @@ -721,7 +725,7 @@ class FirConverter : public AbstractConverter { po.u); } } - void genFIR(const Pa::PointerAssignmentStmt &) { SOFT_TODO(); } + void genFIR(const Pa::PointerAssignmentStmt &) { TODO(); } void genFIR(const Pa::SyncAllStmt &) { // call some runtime routine @@ -912,7 +916,7 @@ class FirConverter : public AbstractConverter { std::visit( [&](auto *p) { genFIR(*p, name, symbol); }, func.funStmts.front()); } else { - name = mangler.doProgramEntry(); + name = uniquer.doProgramEntry(); } startNewFunction(func, name, symbol); @@ -1034,7 +1038,7 @@ class FirConverter : public AbstractConverter { const Co::IntrinsicTypeDefaultKinds &defaults; IntrinsicLibrary intrinsics; M::OpBuilder *builder{nullptr}; - fir::NameMangler &mangler; + fir::NameUniquer &uniquer; SymMap localSymbols; std::list localEdgeQ; LabelMapType localBlockMap; @@ -1049,12 +1053,12 @@ class FirConverter : public AbstractConverter { FirConverter &operator=(const FirConverter &) = delete; virtual ~FirConverter() = default; - explicit FirConverter(BurnsideBridge &bridge, fir::NameMangler &mangler) + explicit FirConverter(BurnsideBridge &bridge, fir::NameUniquer &uniquer) : mlirContext{bridge.getMLIRContext()}, cooked{bridge.getCookedSource()}, module{bridge.getModule()}, defaults{bridge.getDefaultKinds()}, intrinsics{IntrinsicLibrary::create( IntrinsicLibrary::Version::LLVM, bridge.getMLIRContext())}, - mangler{mangler} {} + uniquer{uniquer} {} /// Convert the AST to FIR void run(AST::Program &ast) { @@ -1073,7 +1077,7 @@ class FirConverter : public AbstractConverter { std::visit(common::visitors{ [&](AST::FunctionLikeUnit &f) { lowerFunc(f, {}); }, [&](AST::ModuleLikeUnit &m) { lowerMod(m); }, - [](AST::BlockDataUnit &) { SOFT_TODO(); }, + [&](AST::BlockDataUnit &) { TODO(); }, }, u); } @@ -1083,29 +1087,35 @@ class FirConverter : public AbstractConverter { // AbstractConverter overrides M::Value *genExprAddr( - const SomeExpr &expr, M::Location *loc = nullptr) override { + const SomeExpr &expr, M::Location *loc = nullptr) override final { return createFIRAddr(loc ? *loc : toLocation(), &expr); } M::Value *genExprValue( - const SomeExpr &expr, M::Location *loc = nullptr) override { + const SomeExpr &expr, M::Location *loc = nullptr) override final { return createFIRExpr(loc ? *loc : toLocation(), &expr); } - M::Type genType(const Ev::DataRef &data) override { + M::Type genType(const Ev::DataRef &data) override final { return translateDataRefToFIRType(&mlirContext, defaults, data); } - M::Type genType(const SomeExpr &expr) override { + M::Type genType(const SomeExpr &expr) override final { return translateSomeExprToFIRType(&mlirContext, defaults, &expr); } - M::Type genType(const SymbolRef &sym) override { + M::Type genType(SymbolRef sym) override final { return translateSymbolToFIRType(&mlirContext, defaults, sym); } + M::Type genType(common::TypeCategory tc, int kind) override final { + return getFIRType(&mlirContext, defaults, tc, kind); + } + M::Type genType(common::TypeCategory tc) override final { + return getFIRType(&mlirContext, defaults, tc); + } - M::Location getCurrentLocation() override { return toLocation(); } - M::Location genLocation() override { + M::Location getCurrentLocation() override final { return toLocation(); } + M::Location genLocation() override final { return M::UnknownLoc::get(&mlirContext); } - M::Location genLocation(const Pa::CharBlock &block) override { + M::Location genLocation(const Pa::CharBlock &block) override final { if (cooked) { auto loc{cooked->GetSourcePositionRange(block)}; if (loc.has_value()) { @@ -1118,17 +1128,21 @@ class FirConverter : public AbstractConverter { return genLocation(); } - M::OpBuilder &getOpBuilder() override { return *builder; } - M::ModuleOp &getModuleOp() override { return module; } + M::OpBuilder &getOpBuilder() override final { return *builder; } + M::ModuleOp &getModuleOp() override final { return module; } + + std::string mangleName(SymbolRef symbol) override final { + return mangle::mangleName(uniquer, symbol); + } }; } // namespace void Br::BurnsideBridge::lower( - const Pa::Program &prg, fir::NameMangler &mangler) { + const Pa::Program &prg, fir::NameUniquer &uniquer) { AST::Program *ast{Br::createAST(prg)}; Br::annotateControl(*ast); - FirConverter converter{*this, mangler}; + FirConverter converter{*this, uniquer}; converter.run(*ast); delete ast; } diff --git a/lib/burnside/bridge.h b/lib/burnside/bridge.h index 52fba728845d..16763f262525 100644 --- a/lib/burnside/bridge.h +++ b/lib/burnside/bridge.h @@ -15,6 +15,7 @@ #ifndef FORTRAN_BURNSIDE_BRIDGE_H_ #define FORTRAN_BURNSIDE_BRIDGE_H_ +#include "../common/Fortran.h" #include "mlir/IR/MLIRContext.h" #include "mlir/IR/Module.h" #include @@ -52,7 +53,7 @@ namespace mlir { class OpBuilder; } namespace fir { -struct NameMangler; +struct NameUniquer; } namespace Fortran::burnside { @@ -82,7 +83,11 @@ class AbstractConverter { /// Generate the type of an Expr virtual mlir::Type genType(const SomeExpr &) = 0; /// Generate the type of a Symbol - virtual mlir::Type genType(const SymbolRef &) = 0; + virtual mlir::Type genType(SymbolRef) = 0; + /// Generate the type from a category + virtual mlir::Type genType(common::TypeCategory tc) = 0; + /// Generate the type from a category and kind + virtual mlir::Type genType(common::TypeCategory tc, int kind) = 0; // // Locations @@ -101,6 +106,8 @@ class AbstractConverter { virtual mlir::OpBuilder &getOpBuilder() = 0; /// Get the ModuleOp virtual mlir::ModuleOp &getModuleOp() = 0; + /// Unique a symbol + virtual std::string mangleName(SymbolRef) = 0; virtual ~AbstractConverter() = default; }; @@ -127,7 +134,7 @@ class BurnsideBridge { const parser::CookedSource *getCookedSource() const { return cooked; } /// Cross the bridge from the Fortran parse-tree, etc. to FIR+OpenMP+MLIR - void lower(const parser::Program &program, fir::NameMangler &mangler); + void lower(const parser::Program &program, fir::NameUniquer &uniquer); private: explicit BurnsideBridge(const common::IntrinsicTypeDefaultKinds &defaultKinds, diff --git a/lib/burnside/builder.cc b/lib/burnside/builder.cc index e064fb365641..9f2b834b95f7 100644 --- a/lib/burnside/builder.cc +++ b/lib/burnside/builder.cc @@ -27,54 +27,6 @@ namespace Se = Fortran::semantics; using namespace Fortran; using namespace Fortran::burnside; -#if 0 -// This will need to be extended to consider the type of what is being mangled -std::string B::applyNameMangling(llvm::StringRef parserName) { - // FIXME: this is fake for now, add type info, etc. - return "_Qp_"s + parserName.str(); -} -#endif - -std::string B::applyNameMangling(const Ev::ProcedureDesignator &proc) { - if (const auto *symbol{proc.GetSymbol()}) { - return applyNameMangling(*symbol); - } else { - // Do not mangle intrinsic for now - assert(proc.GetSpecificIntrinsic() && - "expected intrinsic procedure in designator"); - return proc.GetName(); - } -} - -std::string B::applyNameMangling(Se::SymbolRef symbol) { - // FIXME: this is fake for now, add type info, etc. - // For now, only works for external procedures - // TODO: apply binding - // TODO: determine if procedure are: - // - external, internal or module - // TODO: Apply proposed mangling with _Qp_ .... - return std::visit( - common::visitors{ - [&](const Se::MainProgramDetails &) { return "MAIN_"s; }, - [&](const Se::SubprogramDetails &) { - return symbol->name().ToString() + "_"; - }, - [&](const Se::ProcEntityDetails &) { - return symbol->name().ToString() + "_"; - }, - [&](const Se::SubprogramNameDetails &) { - assert(false && - "SubprogramNameDetails not expected after semantic analysis"); - return ""s; - }, - [&](const auto &) { - assert(false && "Symbol mangling TODO"); - return ""s; - }, - }, - symbol->details()); -} - M::FuncOp B::createFunction(B::AbstractConverter &converter, llvm::StringRef name, M::FunctionType funcTy) { auto func{M::FuncOp::create(converter.getCurrentLocation(), name, funcTy)}; diff --git a/lib/burnside/builder.h b/lib/burnside/builder.h index 02eef8144b89..8f663c59f5ee 100644 --- a/lib/burnside/builder.h +++ b/lib/burnside/builder.h @@ -61,9 +61,6 @@ class SymMap { } }; -std::string applyNameMangling(const evaluate::ProcedureDesignator &proc); -std::string applyNameMangling(semantics::SymbolRef symbol); - /// Get the current Module inline mlir::ModuleOp getModule(mlir::OpBuilder *bldr) { return bldr->getBlock()->getParent()->getParentOfType(); diff --git a/lib/burnside/cfg-builder.h b/lib/burnside/cfg-builder.h index a76b4edbd838..754c4314629b 100644 --- a/lib/burnside/cfg-builder.h +++ b/lib/burnside/cfg-builder.h @@ -240,8 +240,8 @@ class CfgBuilder { [&](const Pa::ElseIfStmt *) { doNextIfBlock(evals, e, iter, evals.end()); }, - [](const Pa::WhereConstructStmt *stmt) { TODO(); }, - [](const Pa::MaskedElsewhereStmt *stmt) { TODO(); }, + [](const Pa::WhereConstructStmt *) { TODO(); }, + [](const Pa::MaskedElsewhereStmt *) { TODO(); }, [](auto) { assert(false && "unhandled CGOTO case"); }, }, e.u); @@ -266,39 +266,39 @@ class CfgBuilder { case AST::CFGAnnotation::IoSwitch: std::visit( Co::visitors{ - [](const Pa::BackspaceStmt *stmt) { TODO(); }, - [](const Pa::CloseStmt *stmt) { TODO(); }, - [](const Pa::EndfileStmt *stmt) { TODO(); }, - [](const Pa::FlushStmt *stmt) { TODO(); }, - [](const Pa::InquireStmt *stmt) { TODO(); }, - [](const Pa::OpenStmt *stmt) { TODO(); }, - [](const Pa::ReadStmt *stmt) { TODO(); }, - [](const Pa::RewindStmt *stmt) { TODO(); }, - [](const Pa::WaitStmt *stmt) { TODO(); }, - [](const Pa::WriteStmt *stmt) { TODO(); }, + [](const Pa::BackspaceStmt *) { TODO(); }, + [](const Pa::CloseStmt *) { TODO(); }, + [](const Pa::EndfileStmt *) { TODO(); }, + [](const Pa::FlushStmt *) { TODO(); }, + [](const Pa::InquireStmt *) { TODO(); }, + [](const Pa::OpenStmt *) { TODO(); }, + [](const Pa::ReadStmt *) { TODO(); }, + [](const Pa::RewindStmt *) { TODO(); }, + [](const Pa::WaitStmt *) { TODO(); }, + [](const Pa::WriteStmt *) { TODO(); }, [](auto) { assert(false && "unhandled IO switch case"); }, }, e.u); break; case AST::CFGAnnotation::Switch: std::visit(Co::visitors{ - [](const Pa::CallStmt *stmt) { TODO(); }, - [](const Pa::ArithmeticIfStmt *stmt) { TODO(); }, - [](const Pa::ComputedGotoStmt *stmt) { TODO(); }, - [](const Pa::SelectCaseStmt *stmt) { TODO(); }, - [](const Pa::SelectRankStmt *stmt) { TODO(); }, - [](const Pa::SelectTypeStmt *stmt) { TODO(); }, + [](const Pa::CallStmt *) { TODO(); }, + [](const Pa::ArithmeticIfStmt *) { TODO(); }, + [](const Pa::ComputedGotoStmt *) { TODO(); }, + [](const Pa::SelectCaseStmt *) { TODO(); }, + [](const Pa::SelectRankStmt *) { TODO(); }, + [](const Pa::SelectTypeStmt *) { TODO(); }, [](auto) { assert(false && "unhandled switch case"); }, }, e.u); break; case AST::CFGAnnotation::Iterative: std::visit(Co::visitors{ - [](const Pa::NonLabelDoStmt *stmt) { TODO(); }, - [](const Pa::WhereStmt *stmt) { TODO(); }, - [](const Pa::ForallStmt *stmt) { TODO(); }, - [](const Pa::WhereConstruct *stmt) { TODO(); }, - [](const Pa::ForallConstructStmt *stmt) { TODO(); }, + [](const Pa::NonLabelDoStmt *) { TODO(); }, + [](const Pa::WhereStmt *) { TODO(); }, + [](const Pa::ForallStmt *) { TODO(); }, + [](const Pa::WhereConstruct *) { TODO(); }, + [](const Pa::ForallConstructStmt *) { TODO(); }, [](auto) { assert(false && "unhandled loop case"); }, }, e.u); @@ -319,7 +319,7 @@ class CfgBuilder { } } - void setActualTargets(std::list &evals) { + void setActualTargets(std::list &) { for (auto &lst1 : cfgEdgeSetPool) for (auto *e : lst1) { e->isTarget = true; diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 5e46fdaa5747..240dbc650472 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -13,10 +13,10 @@ // limitations under the License. #include "convert-expr.h" +#include "bridge.h" #include "builder.h" #include "complex-handler.h" #include "convert-type.h" -#include "intrinsics.h" #include "runtime.h" #include "../common/default-kinds.h" #include "../common/unwrap.h" @@ -36,12 +36,8 @@ #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/StandardOps/Ops.h" #include "mlir/IR/Attributes.h" -#include "mlir/IR/Builders.h" -#include "mlir/IR/MLIRContext.h" -#include "mlir/IR/Module.h" #include "mlir/IR/Operation.h" #include "mlir/IR/PatternMatch.h" -#include "mlir/IR/Value.h" #include "mlir/Pass/Pass.h" #include "mlir/Pass/PassManager.h" #include "mlir/Transforms/DialectConversion.h" @@ -67,13 +63,13 @@ namespace { /// Lowering of Fortran::evaluate::Expr expressions class ExprLowering { M::Location location; + AbstractConverter &converter; M::OpBuilder &builder; SomeExpr const &expr; SymMap &symMap; #if 0 SymMap loadedSymbols; #endif - Co::IntrinsicTypeDefaultKinds const &defaults; IntrinsicLibrary const &intrinsics; bool genLogicalAsI1{false}; @@ -113,7 +109,7 @@ class ExprLowering { /// Generate an integral constant of `value` template M::Value *genIntegerConstant(M::MLIRContext *context, std::int64_t value) { - M::Type type{getFIRType(context, defaults, IntegerCat, KIND)}; + M::Type type{converter.genType(IntegerCat, KIND)}; auto attr{builder.getIntegerAttr(type, value)}; auto res{builder.create(getLoc(), type, attr)}; return res.getResult(); @@ -170,8 +166,7 @@ class ExprLowering { // FIXME binary operation :: ('a, 'a) -> 'a template M::FunctionType createFunctionType() { if constexpr (TC == IntegerCat) { - M::Type output{ - getFIRType(builder.getContext(), defaults, IntegerCat, KIND)}; + M::Type output{converter.genType(IntegerCat, KIND)}; L::SmallVector inputs; inputs.push_back(output); inputs.push_back(output); @@ -227,8 +222,8 @@ class ExprLowering { M::Value *gen(Se::SymbolRef sym) { // FIXME: not all symbols are local - return createTemporary(getLoc(), builder, symMap, - translateSymbolToFIRType(builder.getContext(), defaults, sym), &*sym); + return createTemporary( + getLoc(), builder, symMap, converter.genType(sym), &*sym); } M::Value *gendef(Se::SymbolRef sym) { return gen(sym); } @@ -334,7 +329,7 @@ class ExprLowering { M::Value *genval(Ev::Power> const &op) { llvm::SmallVector operands{ genval(op.left()), genval(op.right())}; - M::Type ty{getFIRType(builder.getContext(), defaults, TC, KIND)}; + M::Type ty{converter.genType(TC, KIND)}; return intrinsics.genval(getLoc(), builder, "pow", ty, operands); } template @@ -343,7 +338,7 @@ class ExprLowering { // are ok llvm::SmallVector operands{ genval(op.left()), genval(op.right())}; - M::Type ty{getFIRType(builder.getContext(), defaults, TC, KIND)}; + M::Type ty{converter.genType(TC, KIND)}; return intrinsics.genval(getLoc(), builder, "pow", ty, operands); } @@ -397,7 +392,7 @@ class ExprLowering { template M::Value *genval(Ev::Convert, TC2> const &convert) { - auto ty{getFIRType(builder.getContext(), defaults, TC1, KIND)}; + auto ty{converter.genType(TC1, KIND)}; M::Value *operand{genval(convert.left())}; if (TC1 == LogicalCat && genLogicalAsI1) { // If an i1 result is needed, it does not make sens to convert between @@ -563,7 +558,7 @@ class ExprLowering { L::SmallVector coorArgs; auto obj{gen(*base)}; const Se::Symbol *sym{nullptr}; - M::Type ty{translateSymbolToFIRType(builder.getContext(), defaults, *sym)}; + M::Type ty{converter.genType(*sym)}; for (auto *field : list) { sym = &field->GetLastSymbol(); auto name{sym->name().ToString()}; @@ -654,7 +649,7 @@ class ExprLowering { template M::Value *genval(const Ev::FunctionRef> &funRef) { if (const auto &intrinsic{funRef.proc().GetSpecificIntrinsic()}) { - M::Type ty{getFIRType(builder.getContext(), defaults, TC, KIND)}; + M::Type ty{converter.genType(TC, KIND)}; L::SmallVector operands; // Lower arguments // For now, logical arguments for intrinsic are lowered to `fir.logical` @@ -696,7 +691,7 @@ class ExprLowering { TODO(); } } - M::Type resultType{getFIRType(builder.getContext(), defaults, TC, KIND)}; + M::Type resultType{converter.genType(TC, KIND)}; M::FunctionType funTy{ M::FunctionType::get(argTypes, resultType, builder.getContext())}; M::FuncOp func{getFunction(applyNameMangling(funRef.proc()), funTy)}; @@ -733,8 +728,7 @@ class ExprLowering { } } else if (type.isa()) { if (!genLogicalAsI1) { - M::Type firLogicalType{ - getFIRType(builder.getContext(), defaults, LogicalCat, KIND)}; + M::Type firLogicalType{converter.genType(LogicalCat, KIND)}; result = builder.create(getLoc(), firLogicalType, result); } @@ -754,13 +748,23 @@ class ExprLowering { return {}; } + std::string applyNameMangling(const Ev::ProcedureDesignator &proc) { + if (const auto *symbol{proc.GetSymbol()}) { + return converter.mangleName(*symbol); + } else { + // Do not mangle intrinsic for now + assert(proc.GetSpecificIntrinsic() && + "expected intrinsic procedure in designator"); + return proc.GetName(); + } + } + public: - explicit ExprLowering(M::Location loc, M::OpBuilder &bldr, - SomeExpr const &vop, SymMap &map, - Co::IntrinsicTypeDefaultKinds const &defaults, - IntrinsicLibrary const &intr, bool logicalAsI1 = false) - : location{loc}, builder{bldr}, expr{vop}, symMap{map}, defaults{defaults}, - intrinsics{intr}, genLogicalAsI1{logicalAsI1} {} + explicit ExprLowering(M::Location loc, AbstractConverter &converter, + SomeExpr const &vop, SymMap &map, IntrinsicLibrary const &intr, + bool logicalAsI1 = false) + : location{loc}, converter{converter}, builder{converter.getOpBuilder()}, + expr{vop}, symMap{map}, intrinsics{intr}, genLogicalAsI1{logicalAsI1} {} /// Lower the expression `expr` into MLIR standard dialect M::Value *gen() { return gen(expr); } @@ -769,27 +773,22 @@ class ExprLowering { } // namespace -M::Value *Br::createSomeExpression(M::Location loc, M::OpBuilder &builder, - Ev::Expr const &expr, SymMap &symMap, - Co::IntrinsicTypeDefaultKinds const &defaults, - IntrinsicLibrary const &intrinsics) { - return ExprLowering{loc, builder, expr, symMap, defaults, intrinsics, false} - .genval(); +M::Value *Br::createSomeExpression(M::Location loc, + Br::AbstractConverter &converter, Ev::Expr const &expr, + SymMap &symMap, IntrinsicLibrary const &intrinsics) { + return ExprLowering{loc, converter, expr, symMap, intrinsics, false}.genval(); } -M::Value *Br::createI1LogicalExpression(M::Location loc, M::OpBuilder &builder, - Ev::Expr const &expr, SymMap &symMap, - Co::IntrinsicTypeDefaultKinds const &defaults, - IntrinsicLibrary const &intrinsics) { - return ExprLowering{loc, builder, expr, symMap, defaults, intrinsics, true} - .genval(); +M::Value *Br::createI1LogicalExpression(M::Location loc, + Br::AbstractConverter &converter, Ev::Expr const &expr, + SymMap &symMap, IntrinsicLibrary const &intrinsics) { + return ExprLowering{loc, converter, expr, symMap, intrinsics, true}.genval(); } -M::Value *Br::createSomeAddress(M::Location loc, M::OpBuilder &builder, - Ev::Expr const &expr, SymMap &symMap, - Co::IntrinsicTypeDefaultKinds const &defaults, - IntrinsicLibrary const &intrinsics) { - return ExprLowering{loc, builder, expr, symMap, defaults, intrinsics}.gen(); +M::Value *Br::createSomeAddress(M::Location loc, + Br::AbstractConverter &converter, Ev::Expr const &expr, + SymMap &symMap, IntrinsicLibrary const &intrinsics) { + return ExprLowering{loc, converter, expr, symMap, intrinsics}.gen(); } /// Create a temporary variable diff --git a/lib/burnside/convert-expr.h b/lib/burnside/convert-expr.h index 7c8b7e9136e4..468e9c6a068d 100644 --- a/lib/burnside/convert-expr.h +++ b/lib/burnside/convert-expr.h @@ -44,22 +44,22 @@ class Symbol; namespace burnside { +class AbstractConverter; class SymMap; -mlir::Value *createSomeExpression(mlir::Location loc, mlir::OpBuilder &builder, - evaluate::Expr const &expr, SymMap &symMap, - common::IntrinsicTypeDefaultKinds const &defaults, - IntrinsicLibrary const &intrinsics); +mlir::Value *createSomeExpression(mlir::Location loc, + AbstractConverter &converter, + const evaluate::Expr &expr, SymMap &symMap, + const IntrinsicLibrary &intrinsics); mlir::Value *createI1LogicalExpression(mlir::Location loc, - mlir::OpBuilder &builder, evaluate::Expr const &expr, - SymMap &symMap, common::IntrinsicTypeDefaultKinds const &defaults, - IntrinsicLibrary const &intrinsics); + AbstractConverter &converter, + const evaluate::Expr &expr, SymMap &symMap, + const IntrinsicLibrary &intrinsics); -mlir::Value *createSomeAddress(mlir::Location loc, mlir::OpBuilder &builder, - evaluate::Expr const &expr, SymMap &symMap, - common::IntrinsicTypeDefaultKinds const &defaults, - IntrinsicLibrary const &intrinsics); +mlir::Value *createSomeAddress(mlir::Location loc, AbstractConverter &converter, + const evaluate::Expr &expr, SymMap &symMap, + const IntrinsicLibrary &intrinsics); mlir::Value *createTemporary(mlir::Location loc, mlir::OpBuilder &builder, SymMap &symMap, mlir::Type type, const semantics::Symbol *symbol); diff --git a/lib/burnside/convert-type.cc b/lib/burnside/convert-type.cc index 81c53a2e70da..35b61daac0e0 100644 --- a/lib/burnside/convert-type.cc +++ b/lib/burnside/convert-type.cc @@ -51,8 +51,7 @@ template int64_t toConstant(const Ev::Expr &e) { return {} // one argument template, must be specialized -template -M::Type genFIRType(M::MLIRContext *context, int kind) { +template M::Type genFIRType(M::MLIRContext *, int) { return {}; } diff --git a/lib/burnside/mangler.cc b/lib/burnside/mangler.cc index ee05cc836dd2..1cbca9e387d0 100644 --- a/lib/burnside/mangler.cc +++ b/lib/burnside/mangler.cc @@ -70,21 +70,21 @@ L::Optional hostName(const Se::Scope *scope) { // Mangle the name of `symbol` to make it unique within FIR's symbol table using // the FIR name mangler, `mangler` std::string Ma::mangleName( - fir::NameMangler &mangler, const Se::SymbolRef symbol) { + fir::NameUniquer &uniquer, const Se::SymbolRef symbol) { return std::visit(Co::visitors{ [&](const Se::MainProgramDetails &) { - return mangler.doProgramEntry().str(); + return uniquer.doProgramEntry().str(); }, [&](const Se::SubprogramDetails &) { auto &cb{symbol->name()}; auto modNames{moduleNames(symbol->scope())}; - return mangler.doProcedure(modNames, + return uniquer.doProcedure(modNames, hostName(symbol->scope()), toStringRef(cb)); }, [&](const Se::ProcEntityDetails &) { auto &cb{symbol->name()}; auto modNames{moduleNames(symbol->scope())}; - return mangler.doProcedure(modNames, + return uniquer.doProcedure(modNames, hostName(symbol->scope()), toStringRef(cb)); }, [](const auto &) -> std::string { @@ -96,5 +96,6 @@ std::string Ma::mangleName( } std::string Ma::demangleName(L::StringRef name) { - return name.str(); + auto result{fir::NameUniquer::deconstruct(name)}; + return result.second.name; } diff --git a/lib/burnside/mangler.h b/lib/burnside/mangler.h index 145640ac5135..11da39c38d4b 100644 --- a/lib/burnside/mangler.h +++ b/lib/burnside/mangler.h @@ -18,7 +18,7 @@ #include namespace fir { -struct NameMangler; +struct NameUniquer; } namespace llvm { @@ -40,7 +40,7 @@ using SymbolRef = common::Reference; namespace mangle { /// Convert a front-end Symbol to an internal name -std::string mangleName(fir::NameMangler &mangler, const SymbolRef symbol); +std::string mangleName(fir::NameUniquer &uniquer, const SymbolRef symbol); std::string demangleName(llvm::StringRef name); diff --git a/lib/fir/InternalNames.cpp b/lib/fir/InternalNames.cpp index b9a47cfd24b0..f6b160cc44e9 100644 --- a/lib/fir/InternalNames.cpp +++ b/lib/fir/InternalNames.cpp @@ -13,9 +13,8 @@ // limitations under the License. #include "fir/InternalNames.h" -#include "llvm/ADT/ArrayRef.h" +#include "mlir/IR/Diagnostics.h" #include "llvm/ADT/Optional.h" -#include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" namespace L = llvm; @@ -47,18 +46,18 @@ std::string doModulesHost(L::ArrayRef mods, } // namespace -L::StringRef fir::NameMangler::toLower(L::StringRef name) { +L::StringRef fir::NameUniquer::toLower(L::StringRef name) { auto lo = name.lower(); if (name.equals(lo)) return name; return cache.insert(lo).first->getKey(); } -L::StringRef fir::NameMangler::addAsString(std::int64_t i) { +L::StringRef fir::NameUniquer::addAsString(std::int64_t i) { return cache.insert(std::to_string(i)).first->getKey(); } -std::string fir::NameMangler::doKind(std::int64_t kind) { +std::string fir::NameUniquer::doKind(std::int64_t kind) { if (kind < 0) { L::Twine result = "KN" + addAsString(-kind); return result.str(); @@ -67,25 +66,25 @@ std::string fir::NameMangler::doKind(std::int64_t kind) { return result.str(); } -std::string fir::NameMangler::doKinds(L::ArrayRef kinds) { +std::string fir::NameUniquer::doKinds(L::ArrayRef kinds) { L::Twine result; for (auto i : kinds) result.concat(doKind(i)); return result.str(); } -std::string fir::NameMangler::doCommonBlock(L::StringRef name) { +std::string fir::NameUniquer::doCommonBlock(L::StringRef name) { L::Twine result = prefix() + "B" + toLower(name); return result.str(); } -std::string fir::NameMangler::doConstant(L::ArrayRef modules, +std::string fir::NameUniquer::doConstant(L::ArrayRef modules, L::StringRef name) { L::Twine result = prefix() + doModules(modules) + "EC" + toLower(name); return result.str(); } -std::string fir::NameMangler::doDispatchTable(L::ArrayRef modules, +std::string fir::NameUniquer::doDispatchTable(L::ArrayRef modules, L::Optional host, L::StringRef name, L::ArrayRef kinds) { @@ -94,15 +93,15 @@ std::string fir::NameMangler::doDispatchTable(L::ArrayRef modules, return result.str(); } -std::string fir::NameMangler::doGenerated(L::StringRef name) { +std::string fir::NameUniquer::doGenerated(L::StringRef name) { L::Twine result = prefix() + "Q" + toLower(name); return result.str(); } -std::string fir::NameMangler::doIntrinsicTypeDescriptor( +std::string fir::NameUniquer::doIntrinsicTypeDescriptor( L::ArrayRef modules, L::Optional host, IntrinsicType type, std::int64_t kind) { - char const *name; + const char *name{nullptr}; switch (type) { case IntrinsicType::CHARACTER: name = "character"; @@ -125,7 +124,7 @@ std::string fir::NameMangler::doIntrinsicTypeDescriptor( return result.str(); } -std::string fir::NameMangler::doProcedure(L::ArrayRef modules, +std::string fir::NameUniquer::doProcedure(L::ArrayRef modules, L::Optional host, L::StringRef name) { L::Twine result = @@ -133,7 +132,7 @@ std::string fir::NameMangler::doProcedure(L::ArrayRef modules, return result.str(); } -std::string fir::NameMangler::doType(L::ArrayRef modules, +std::string fir::NameUniquer::doType(L::ArrayRef modules, L::Optional host, L::StringRef name, L::ArrayRef kinds) { @@ -142,7 +141,7 @@ std::string fir::NameMangler::doType(L::ArrayRef modules, return result.str(); } -std::string fir::NameMangler::doTypeDescriptor( +std::string fir::NameUniquer::doTypeDescriptor( L::ArrayRef modules, L::Optional host, L::StringRef name, L::ArrayRef kinds) { L::Twine result = prefix() + doModulesHost(modules, host) + "CT" + @@ -150,8 +149,14 @@ std::string fir::NameMangler::doTypeDescriptor( return result.str(); } -std::string fir::NameMangler::doVariable(L::ArrayRef modules, +std::string fir::NameUniquer::doVariable(L::ArrayRef modules, L::StringRef name) { L::Twine result = prefix() + doModules(modules) + "E" + toLower(name); return result.str(); } + +std::pair +fir::NameUniquer::deconstruct(L::StringRef uniquedName) { + assert(false && "not yet implemented"); + return {}; +} diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index 38a42db10dca..38f9643f4c7e 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -78,8 +78,8 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { static L::StringMap identStructCache; public: - FIRToLLVMTypeConverter(M::MLIRContext *context, NameMangler &mangler) - : LLVMTypeConverter(context), kindMapping(context), mangler(mangler) {} + FIRToLLVMTypeConverter(M::MLIRContext *context, NameUniquer &uniquer) + : LLVMTypeConverter(context), kindMapping(context), uniquer(uniquer) {} // This returns the type of a single column. Rows are added by the caller. // fir.dims --> llvm<"[r x [3 x i64]]"> @@ -311,7 +311,7 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { return wrappedLLVMType; } - NameMangler &mangler; + NameUniquer &uniquer; }; // instantiate static data member @@ -1330,7 +1330,7 @@ struct GenTypeDescOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto gentypedesc = M::cast(op); auto ty = unwrap(convertType(gentypedesc.getInType())).getPointerTo(); - std::string name = "fixme"; // FIXME: get the mangled name + std::string name = "fixme"; // FIXME: get the uniqued name rewriter.replaceOpWithNewOp(gentypedesc, ty, name); return matchSuccess(); } @@ -1896,14 +1896,14 @@ struct NegcOpConversion : public FIROpConversion { /// This pass lowers all FIR dialect operations to LLVM IR dialect. An /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. struct FIRToLLVMLoweringPass : public M::ModulePass { - FIRToLLVMLoweringPass(NameMangler &mangler) : mangler{mangler} {} + FIRToLLVMLoweringPass(NameUniquer &uniquer) : uniquer{uniquer} {} void runOnModule() override { if (ClDisableFirToLLVMIR) return; auto *context{&getContext()}; - FIRToLLVMTypeConverter typeConverter{context, mangler}; + FIRToLLVMTypeConverter typeConverter{context, uniquer}; M::OwningRewritePatternList patterns; patterns.insert< AddcOpConversion, AddfOpConversion, AddrOfOpConversion, @@ -1955,7 +1955,7 @@ struct FIRToLLVMLoweringPass : public M::ModulePass { } } - NameMangler &mangler; + NameUniquer &uniquer; }; /// Lower from LLVM IR dialect to proper LLVM-IR and dump the module @@ -1986,8 +1986,8 @@ struct LLVMIRLoweringPass : public M::ModulePass { } // namespace std::unique_ptr -fir::createFIRToLLVMPass(fir::NameMangler &nameMangler) { - return std::make_unique(nameMangler); +fir::createFIRToLLVMPass(fir::NameUniquer &nameUniquer) { + return std::make_unique(nameUniquer); } std::unique_ptr fir::createLLVMDialectToLLVMPass(L::StringRef output) { diff --git a/tools/bbc/bbc.cpp b/tools/bbc/bbc.cpp index d21bfbeea490..372ad88370de 100644 --- a/tools/bbc/bbc.cpp +++ b/tools/bbc/bbc.cpp @@ -269,10 +269,10 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, } // MLIR+FIR - fir::NameMangler nameMangler; + fir::NameUniquer nameUniquer; auto burnside = Br::BurnsideBridge::create( semanticsContext.defaultKinds(), &parsing.cooked()); - burnside.lower(parseTree, nameMangler); + burnside.lower(parseTree, nameUniquer); mlir::ModuleOp mlirModule{burnside.getModule()}; mlir::PassManager pm{mlirModule.getContext()}; if (driver.dumpHLFIR) { @@ -289,7 +289,7 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, pm.addPass(fir::createLowerToLoopPass()); pm.addPass(fir::createFIRToStdPass()); pm.addPass(mlir::createLowerToCFGPass()); - pm.addPass(fir::createFIRToLLVMPass(nameMangler)); + pm.addPass(fir::createFIRToLLVMPass(nameUniquer)); } if (driver.lowerToLLVMIR) { pm.addPass(fir::createLLVMDialectToLLVMPass("a.ll")); diff --git a/tools/tco/CMakeLists.txt b/tools/tco/CMakeLists.txt index 6037e10ea66a..4c927d8548c3 100644 --- a/tools/tco/CMakeLists.txt +++ b/tools/tco/CMakeLists.txt @@ -37,7 +37,7 @@ set(EXE_LIBS MLIRSupport MLIRTransforms MLIRVectorOps - MLIRVectorConversions + MLIRVectorToLLVM ) add_llvm_tool(tco tco.cpp) diff --git a/tools/tco/tco.cpp b/tools/tco/tco.cpp index dbb8bbbe7709..b51b0be2481d 100644 --- a/tools/tco/tco.cpp +++ b/tools/tco/tco.cpp @@ -67,7 +67,7 @@ int compileFIR() { owningRef->dump(); // run passes - fir::NameMangler mangler; + fir::NameUniquer uniquer; mlir::PassManager pm{context.get()}; pm.addPass(fir::createMemToRegPass()); pm.addPass(fir::createCSEPass()); @@ -78,7 +78,7 @@ int compileFIR() { pm.addPass(fir::createFIRToStdPass()); // convert loop dialect to standard pm.addPass(mlir::createLowerToCFGPass()); - pm.addPass(fir::createFIRToLLVMPass(mangler)); + pm.addPass(fir::createFIRToLLVMPass(uniquer)); pm.addPass(fir::createLLVMDialectToLLVMPass(ClOutput)); if (mlir::succeeded(pm.run(*owningRef))) { errs() << ";== output ==\n"; From ad775cb846eff5eda7c9a3458b4a3952f8bf075a Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Fri, 13 Dec 2019 04:46:49 -0800 Subject: [PATCH 068/123] MERGE lowering and MIN/MAX/CONJG/Comparisons lowering update - MERGE: use MLIR `SelectOP` - MIN/MAX: also use `SelectOp`. Provide different ways to compare MIN/MAX operands to match existing behaviors with NaNs. Use Minss/Maxss for now because it matches ifort, nagfor and pgfortran -noillvm behavior. - CONJG: use `fir::NegfOp` instead of a substraction - Comparisons: Changed Real comparions to ordered comparions but for .NE. that remains `UNE`. This matches other compilers behavior and is closer from Fortran 2018 table 17.1 requirements in IEEE contexts. Comments regarding failure to fulfill IEEE requirements regarding the signaling/quiet aspect of the result for MIN/MAX/Comparisons is added (Need llvm update or custom runtime). --- lib/burnside/convert-expr.cc | 20 +++-- lib/burnside/intrinsics.cc | 137 ++++++++++++++++++++++++++--------- 2 files changed, 114 insertions(+), 43 deletions(-) diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 240dbc650472..93977060607e 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -90,17 +90,23 @@ class ExprLowering { return {}; } - /// Convert parser's REAL relational operators to MLIR. TODO: using - /// unordered, but we may want to cons ordered in certain situation. + /// Convert parser's REAL relational operators to MLIR. + /// The choice of order (O prefix) vs unorder (U prefix) follows Fortran 2018 + /// requirements in the IEEE context (table 17.1 of F2018). This choice is + /// also applied in other contexts because it is easier and in line with + /// other Fortran compilers. + /// FIXME: The signaling/quiet aspect of the table 17.1 requirement is not + /// fully enforced. FIR and LLVM `fcmp` instructions do not give any guarantee + /// whether the comparison will signal or not in case of quiet NaN argument. static fir::CmpFPredicate translateFloatRelational( Co::RelationalOperator rop) { switch (rop) { - case Co::RelationalOperator::LT: return fir::CmpFPredicate::ULT; - case Co::RelationalOperator::LE: return fir::CmpFPredicate::ULE; - case Co::RelationalOperator::EQ: return fir::CmpFPredicate::UEQ; + case Co::RelationalOperator::LT: return fir::CmpFPredicate::OLT; + case Co::RelationalOperator::LE: return fir::CmpFPredicate::OLE; + case Co::RelationalOperator::EQ: return fir::CmpFPredicate::OEQ; case Co::RelationalOperator::NE: return fir::CmpFPredicate::UNE; - case Co::RelationalOperator::GT: return fir::CmpFPredicate::UGT; - case Co::RelationalOperator::GE: return fir::CmpFPredicate::UGE; + case Co::RelationalOperator::GT: return fir::CmpFPredicate::OGT; + case Co::RelationalOperator::GE: return fir::CmpFPredicate::OGE; } assert(false && "unhandled REAL relational operator"); return {}; diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index 753d30b7e7f8..253a973c146a 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -72,8 +72,47 @@ class MathRuntimeLibrary { Map library; }; -/// enum used to templatized code gen for intrinsics that are alike. +/// Enums used to templatize and share lowering of MIN and MAX. enum class Extremum { Min, Max }; +// There are different ways to deal with NaNs in MIN and MAX. +// Known existing behaviors are listed below and can be selected for +// f18 MIN/MAX implementation. +enum class ExtremumBehavior { + // Note: the Signaling/quiet aspect of NaNs in the behaviors below are + // not described because there is no way to control/observe such aspect in + // MLIR/LLVM yet. The IEEE behaviors come with requirements regarding this + // aspect that are therefore currently not enforced. In the descriptions + // below, NaNs can be signaling or quite. Returned NaNs may be signaling + // if one of the input NaN was signaling but it cannot be guaranteed either. + // Existing compilers using an IEEE behavior (gfortran) also do not fulfill + // signaling/quiet requirements. + IeeeMinMaximumNumber, + // IEEE minimumNumber/maximumNumber behavior (754-2019, section 9.6): + // If one of the argument is and number and the other is NaN, return the + // number. If both arguements are NaN, return NaN. + // Compilers: gfortran. + IeeeMinMaximum, + // IEEE minimum/maximum behavior (754-2019, section 9.6): + // If one of the argument is NaN, return NaN. + MinMaxss, + // x86 minss/maxss behavior: + // If the second argument is a number and the other is NaN, return the number. + // In all other cases where at least one operand is NaN, return NaN. + // Compilers: xlf (only for MAX), ifort, pgfortran -nollvm, and nagfor. + PgfortranLlvm, + // "Opposite of" x86 minss/maxss behavior: + // If the first argument is a number and the other is NaN, return the + // number. + // In all other cases where at least one operand is NaN, return NaN. + // Compilers: xlf (only for MIN), and pgfortran (with llvm). + IeeeMinMaxNum + // IEEE minNum/maxNum behavior (754-2008, section 5.3.1): + // TODO: Not implemented. + // It is the only behavior where the signaling/quiet aspect of a NaN argument + // impacts if the result should be NaN or the argument that is a number. + // LLVM/MLIR do not provide ways to observe this aspect, so it is not + // possible to implement it without some target dependent runtime. +}; /// The implementation of IntrinsicLibrary is based on a map that associates /// Fortran intrinsics generic names to the related FIR generator functions. @@ -141,7 +180,9 @@ class IntrinsicLibrary::Implementation { return genWrapperCall<&I::genRuntimeCall>(c); } mlir::Value *genConjg(Context &) const; - template mlir::Value *genExtremum(Context &) const; + template + mlir::Value *genExtremum(Context &) const; + mlir::Value *genMerge(Context &) const; struct IntrinsicHanlder { const char *name; @@ -154,8 +195,9 @@ class IntrinsicLibrary::Implementation { /// be attempted. static constexpr IntrinsicHanlder handlers[]{ {"conjg", &I::genConjg}, - {"max", &I::genExtremum}, - {"min", &I::genExtremum}, + {"max", &I::genExtremum}, + {"min", &I::genExtremum}, + {"merge", &I::genMerge}, }; // helpers @@ -577,41 +619,71 @@ mlir::Value *IntrinsicLibrary::Implementation::genConjg( mlir::OpBuilder &builder{*genCtxt.builder}; ComplexHandler cplxHandler{builder, genCtxt.loc}; - // TODO a negation unary would be better than a sub to zero ? mlir::Value *cplx{genCtxt.arguments[0]}; - mlir::Type realType{cplxHandler.getComplexPartType(cplx)}; - mlir::Value *zero{builder.create( - genCtxt.loc, realType, builder.getZeroAttr(realType))}; mlir::Value *imag{cplxHandler.extract(cplx)}; - mlir::Value *negImag{ - genCtxt.builder->create(genCtxt.loc, zero, imag)}; + mlir::Value *negImag{genCtxt.builder->create(genCtxt.loc, imag)}; return cplxHandler.insert(cplx, negImag); } -static mlir::FuncOp getMergeFunc(mlir::Type type, mlir::ModuleOp module) { - llvm::SmallVector argTypes{ - type, type, fir::LogicalType::get(module.getContext(), 4)}; - auto mergeType{ - mlir::FunctionType::get(argTypes, {type}, module.getContext())}; - auto mergeName{getIntrinsicWrapperName("merge", mergeType)}; - return createFunction(module, mergeName, mergeType); +// MERGE +mlir::Value *IntrinsicLibrary::Implementation::genMerge( + Context &genCtxt) const { + assert(genCtxt.arguments.size() == 3); + mlir::Type resType{genCtxt.getResultType()}; + mlir::OpBuilder &builder{*genCtxt.builder}; + + auto *trueVal{genCtxt.arguments[0]}; + auto *falseVal{genCtxt.arguments[1]}; + auto *mask{genCtxt.arguments[2]}; + mlir::Type i1Type{mlir::IntegerType::get(1, builder.getContext())}; + mask = builder.create(genCtxt.loc, i1Type, mask); + return builder.create(genCtxt.loc, mask, trueVal, falseVal); } -template -static mlir::Value *createCompare(mlir::Location loc, mlir::OpBuilder &builder, - mlir::Value *left, mlir::Value *right) { +// Compare two FIR values and return boolean result as i1. +template +static mlir::Value *createExtremumCompare(mlir::Location loc, + mlir::OpBuilder &builder, mlir::Value *left, mlir::Value *right) { static constexpr auto integerPredicate{extremum == Extremum::Max ? mlir::CmpIPredicate::sgt : mlir::CmpIPredicate::slt}; - static constexpr auto realPredicate{extremum == Extremum::Max + static constexpr auto unorderedCmp{extremum == Extremum::Max ? fir::CmpFPredicate::UGT : fir::CmpFPredicate::ULT}; + static constexpr auto orderedCmp{extremum == Extremum::Max + ? fir::CmpFPredicate::OGT + : fir::CmpFPredicate::OLT}; auto type{left->getType()}; mlir::Value *result{nullptr}; if (type.isa() || type.isa()) { - // TODO: The type we get from CmpfOp is a bit weird (it is the operands - // types, and not a boolean). - result = builder.create(loc, realPredicate, left, right); + // Note: the signaling/quit aspect of the result required by IEEE + // cannot currently be obtained with LLVM without ad-hoc runtime. + if constexpr (behavior == ExtremumBehavior::IeeeMinMaximumNumber) { + // Return the number if one of the inputs is NaN and the other is + // a number. + auto leftIsResult{ + builder.create(loc, orderedCmp, left, right)}; + auto rightIsNan{builder.create( + loc, fir::CmpFPredicate::UNE, right, right)}; + result = builder.create(loc, leftIsResult, rightIsNan); + } else if constexpr (behavior == ExtremumBehavior::IeeeMinMaximum) { + // Always return NaNs if one the input is NaNs + auto leftIsResult{ + builder.create(loc, orderedCmp, left, right)}; + auto leftIsNan{builder.create( + loc, fir::CmpFPredicate::UNE, left, left)}; + result = builder.create(loc, leftIsResult, leftIsNan); + } else if constexpr (behavior == ExtremumBehavior::MinMaxss) { + // If the left is a NaN, return the right whatever it is. + result = builder.create(loc, orderedCmp, left, right); + } else if constexpr (behavior == ExtremumBehavior::PgfortranLlvm) { + // If one of the operand is a NaN, return left whatever it is. + result = builder.create(loc, unorderedCmp, left, right); + } else { + // TODO: ieeMinNum/ieeeMaxNum + static_assert(behavior == ExtremumBehavior::IeeeMinMaxNum, + "ieeeMinNum/ieeMaxNum behavior not implemented"); + } } else if (type.isa()) { result = builder.create(loc, integerPredicate, left, right); } else if (type.isa()) { @@ -620,28 +692,21 @@ static mlir::Value *createCompare(mlir::Location loc, mlir::OpBuilder &builder, // So we may need a temp. } assert(result); - // TODO which logical type should comparisons return ? - // This is also currently not observed in convert-expr - auto resTy{fir::LogicalType::get(getModule(&builder).getContext(), 4)}; - return builder.create(loc, resTy, result); + return result; } // MIN and MAX -// Extremum intrinsic are lowered using the MERGE intrinsic -// to avoid introducing branches. -template +template mlir::Value *IntrinsicLibrary::Implementation::genExtremum( Context &genCtxt) const { auto &builder{*genCtxt.builder}; auto loc{genCtxt.loc}; assert(genCtxt.arguments.size() >= 2); auto *result{genCtxt.arguments[0]}; - auto mergeFunc{getMergeFunc(result->getType(), genCtxt.getModuleOp())}; for (auto *arg : genCtxt.arguments.drop_front()) { - auto mask{createCompare(loc, builder, result, arg)}; - llvm::SmallVector mergeArgs{result, arg, mask}; - result = - builder.create(loc, mergeFunc, mergeArgs).getResult(0); + auto mask{ + createExtremumCompare(loc, builder, result, arg)}; + result = builder.create(loc, mask, result, arg); } return result; } From 6ea52c29f66901a048dd7fadf4d12a1b26412e17 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Fri, 13 Dec 2019 13:49:55 -0800 Subject: [PATCH 069/123] Fixes to the CMake files to enable in-tree and out-of-tree building. Out-of-tree also requires changes a few changes to MLIR to get all the pieces installed for use. How To Notes: 1. For an in-tree build, we cloned the monorepo and put a link to mlir in llvm/projects and a link to f18 named flang in the top-level. The usual process should work. mkdir build ; cd build export CC= export CXX= cmake ../llvm -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD=X86 -DCMAKE_CXX_STANDARD=17 -DLLVM_ENABLE_PROJECTS=flang -DCMAKE_INSTALL_PREFIX=/tmp make 2. For an out-of-tree build, we have two steps. First, build and install LLVM and MLIR. Second, build f18. For part one, we still need to link mlir in llvm/projects. No flang link is needed. mkdir build ; cd build export CC= export CXX= cmake ../llvm -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD=X86 -DCMAKE_CXX_STANDARD=17 -DCMAKE_INSTALL_PREFIX=/tmp/llvm make make install For part two, we create a second build area and build the f18 bits there. cd .../flang-compiler/f18 ; mkdir build ; cd build PATH=/tmp/llvm/bin:$PATH cmake .. -DLLVM_DIR=/tmp/llvm -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/flang -DLLVM_BUILD_TOOLS=On make --- include/fir/CMakeLists.txt | 4 ++++ lib/burnside/CMakeLists.txt | 2 +- lib/fir/CMakeLists.txt | 2 ++ tools/CMakeLists.txt | 1 - tools/bbc/CMakeLists.txt | 1 + tools/tco/CMakeLists.txt | 1 + 6 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/fir/CMakeLists.txt b/include/fir/CMakeLists.txt index 9528b1abffb0..1e75d7e3efbc 100644 --- a/include/fir/CMakeLists.txt +++ b/include/fir/CMakeLists.txt @@ -1,3 +1,7 @@ +if (LLVM_DIR) + include(AddMLIR) +endif() +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error") set(LLVM_TARGET_DEFINITIONS FIROps.td) mlir_tablegen(FIROps.h.inc -gen-op-decls) mlir_tablegen(FIROps.cpp.inc -gen-op-defs) diff --git a/lib/burnside/CMakeLists.txt b/lib/burnside/CMakeLists.txt index 9844363ac39e..1c004e731e96 100644 --- a/lib/burnside/CMakeLists.txt +++ b/lib/burnside/CMakeLists.txt @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error") add_library(FortranBurnside ast-builder.cc diff --git a/lib/fir/CMakeLists.txt b/lib/fir/CMakeLists.txt index 54587daaf7f1..cb11645a22c0 100644 --- a/lib/fir/CMakeLists.txt +++ b/lib/fir/CMakeLists.txt @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error") + add_subdirectory(Transforms) add_llvm_library(FIR diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 76a1c1ab9d3e..49d59193190e 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -6,7 +6,6 @@ # #===------------------------------------------------------------------------===# - add_subdirectory(f18) add_subdirectory(bbc) add_subdirectory(tco) diff --git a/tools/bbc/CMakeLists.txt b/tools/bbc/CMakeLists.txt index a13b7b96d446..36f351529fd1 100644 --- a/tools/bbc/CMakeLists.txt +++ b/tools/bbc/CMakeLists.txt @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter -Wno-error") set(LIBS FIR MLIRAffineOps diff --git a/tools/tco/CMakeLists.txt b/tools/tco/CMakeLists.txt index 4c927d8548c3..115f949ae573 100644 --- a/tools/tco/CMakeLists.txt +++ b/tools/tco/CMakeLists.txt @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter -Wno-error") set(LIBS FIR MLIRAffineOps From b7e12516cd7baf5fb3873214717848048e840fef Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Wed, 18 Dec 2019 07:58:18 -0800 Subject: [PATCH 070/123] Lowering Character dummy argument to boxchar type This commit does it for all character dummy arguments. IsDescriptor need to be changed or another function used to determine when a descriptor is needed. Do not make a difference between external/internal procedure for now. --- lib/burnside/bridge.cc | 31 +++++++--------------- lib/burnside/convert-type.cc | 50 +++++++++++++++++++++++++++++------- lib/burnside/convert-type.h | 3 +++ 3 files changed, 53 insertions(+), 31 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 53ef8b7a2518..24fa8975492f 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -52,8 +52,7 @@ L::cl::opt ClDisableToDoAssert("disable-burnside-todo", L::cl::Hidden); #undef TODO -#define TODO() \ - assert(false && "not implemented yet") +#define TODO() assert(false && "not implemented yet") using SelectCaseConstruct = Pa::CaseConstruct; using SelectRankConstruct = Pa::SelectRankConstruct; @@ -342,9 +341,7 @@ class FirConverter : public AbstractConverter { void genFIRIOSwitch(AST::Evaluation &) { TODO(); } // Iterative loop control-flow semantics - void genFIREvalIterative(AST::Evaluation &) { - TODO(); - } + void genFIREvalIterative(AST::Evaluation &) { TODO(); } void switchInsertionPointToWhere(fir::WhereOp &where) { builder->setInsertionPointToStart(&where.whereRegion().front()); @@ -514,7 +511,7 @@ class FirConverter : public AbstractConverter { auto funTy{M::FunctionType::get(argTy, resTy, builder->getContext())}; // FIXME: mangle name M::FuncOp func{getFunc(funName, funTy)}; - (void)func; // FIXME + (void)func; // FIXME std::vector actuals; for (auto &aa : std::get>(stmt.v.t)) { auto &kw = std::get>(aa.t); @@ -836,22 +833,8 @@ class FirConverter : public AbstractConverter { // get arguments and return type if any, otherwise just use empty vectors L::SmallVector args; L::SmallVector results; - if (symbol) { - auto *details{symbol->detailsIf()}; - assert(details && "details for semantics::Symbol must be subprogram"); - for (auto *a : details->dummyArgs()) { - if (a) { // nullptr indicates alternate return argument - auto type{genType(*a)}; - args.push_back(fir::ReferenceType::get(type)); - } - } - if (details->isFunction()) { - // FIXME: handle subroutines that return magic values - auto result{details->result()}; - results.push_back(genType(result)); - } - } - auto funcTy{M::FunctionType::get(args, results, &mlirContext)}; + auto funcTy{symbol ? genFunctionType(*symbol) + : M::FunctionType::get(args, results, &mlirContext)}; return createFunction(*this, name, funcTy); } @@ -1083,6 +1066,10 @@ class FirConverter : public AbstractConverter { } } + M::FunctionType genFunctionType(SymbolRef sym) { + return translateSymbolToFIRFunctionType(&mlirContext, defaults, sym); + } + // // AbstractConverter overrides diff --git a/lib/burnside/convert-type.cc b/lib/burnside/convert-type.cc index 35b61daac0e0..173f82d3d213 100644 --- a/lib/burnside/convert-type.cc +++ b/lib/burnside/convert-type.cc @@ -247,24 +247,51 @@ class TypeBuilder { return bounds; } - /// Type consing from a symbol. A symbol's type must be created from the type - /// discovered by the front-end at runtime. - M::Type gen(Se::SymbolRef symbol) { + M::Type genDummyArgType(const Se::Symbol &dummy) { + if (auto *type{dummy.GetType()}) { + auto *tySpec{type->AsIntrinsic()}; + if (tySpec && tySpec->category() == CharacterCat) { + auto kind = toConstant(tySpec->kind()); + return fir::BoxCharType::get(context, kind); + } + } + if (Se::IsDescriptor(dummy)) { + // FIXME: This should be the first case, but it seems to + // fire at assumed length character on purpose which is + // not what I expect. + TODO(); + } + return fir::ReferenceType::get(gen(dummy)); + } + + M::FunctionType genFunctionType(Se::SymbolRef symbol) { + llvm::SmallVector returnTys; + llvm::SmallVector inputTys; if (auto *proc = symbol->detailsIf()) { - M::Type returnTy{mkVoid()}; if (proc->isFunction()) { - returnTy = gen(proc->result()); + returnTys.emplace_back(gen(proc->result())); } // FIXME: handle alt-return - llvm::SmallVector inputTys; for (auto *arg : proc->dummyArgs()) { - // FIXME: not all args are pass by ref // Nullptr args are alternate returns indicators if (arg) { - inputTys.emplace_back(fir::ReferenceType::get(gen(*arg))); + inputTys.emplace_back(genDummyArgType(*arg)); } } - return M::FunctionType::get(inputTys, returnTy, context); + } else if (auto *proc = symbol->detailsIf()) { + // TODO Should probably use evaluate::Characteristics for that. + TODO(); + } else { + assert(false && "unexpected symbol details for function"); + } + return M::FunctionType::get(inputTys, returnTys, context); + } + + /// Type consing from a symbol. A symbol's type must be created from the type + /// discovered by the front-end at runtime. + M::Type gen(Se::SymbolRef symbol) { + if (symbol->detailsIf()) { + return genFunctionType(symbol); } M::Type returnTy{}; if (auto *type{symbol->GetType()}) { @@ -366,6 +393,11 @@ M::Type Br::translateSymbolToFIRType(M::MLIRContext *context, return TypeBuilder{context, defaults}.gen(symbol); } +M::FunctionType Br::translateSymbolToFIRFunctionType(M::MLIRContext *context, + Co::IntrinsicTypeDefaultKinds const &defaults, const SymbolRef symbol) { + return TypeBuilder{context, defaults}.genFunctionType(symbol); +} + M::Type Br::convertReal(M::MLIRContext *context, int kind) { return genFIRType(context, kind); } diff --git a/lib/burnside/convert-type.h b/lib/burnside/convert-type.h index 658340396c82..a842f0862782 100644 --- a/lib/burnside/convert-type.h +++ b/lib/burnside/convert-type.h @@ -98,6 +98,9 @@ mlir::Type translateSomeExprToFIRType(mlir::MLIRContext *ctxt, mlir::Type translateSymbolToFIRType(mlir::MLIRContext *ctxt, common::IntrinsicTypeDefaultKinds const &defaults, const SymbolRef symbol); +mlir::FunctionType translateSymbolToFIRFunctionType(mlir::MLIRContext *ctxt, + common::IntrinsicTypeDefaultKinds const &defaults, const SymbolRef symbol); + mlir::Type convertReal(mlir::MLIRContext *ctxt, int KIND); } // burnside From 8688602d0560cc3b3af0ce6470c1311d3b699309 Mon Sep 17 00:00:00 2001 From: V Donaldson Date: Wed, 18 Dec 2019 10:23:30 -0800 Subject: [PATCH 071/123] Implement an -fdebug-dump-pre-fir option to dump FIR AST code. Dump output reflects the nesting structure of the AST. For example, an IF statement appears at the same level as a dependent assignment statement, with both nested under an IF construct. Example: subroutine s(a, b) integer :: a, b if (a .eq. 1) then b = 1 else if (a .lt. -4) then b = -1 else b = 42 end if end subroutine s subroutine sub(nn) do kk = 1, nn block do ii = 1, 3 if (ii .eq. 3) then block integer jj jj = -3 print*, jj endblock endif print*, kk, ii enddo endblock enddo print*, 'ok' end call s(1, 7) call sub(2) end $ bbc -fdebug-dump-pre-fir -fno-dump-hl-fir -fno-dump-fir file.f90 Subroutine s: subroutine s(a, b) <> IfThenStmt: if(a .eq. 1) then AssignmentStmt: b = 1 ElseIfStmt: else if(a .lt. -4) then AssignmentStmt: b = -1 ElseStmt: else AssignmentStmt: b = 42 EndIfStmt: end if <> EndSubroutine s Subroutine sub: subroutine sub(nn) <> NonLabelDoStmt: do kk = 1, nn <> BlockStmt: block <> NonLabelDoStmt: do ii = 1, 3 <> IfThenStmt: if(ii .eq. 3) then <> BlockStmt: block AssignmentStmt: jj = -3 PrintStmt: print*, jj EndBlockStmt: endblock <> EndIfStmt: endif <> PrintStmt: print*, kk, ii EndDoStmt: enddo <> EndBlockStmt: endblock <> EndDoStmt: enddo <> PrintStmt: print*, 'ok' EndSubroutine sub Program CallStmt: call s(1, 7) CallStmt: call sub(2) EndProgram --- lib/burnside/ast-builder.cc | 182 ++++++++++++++++++++++++++++++++++++ lib/burnside/ast-builder.h | 3 + lib/burnside/bridge.cc | 3 + lib/burnside/bridge.h | 4 + tools/bbc/bbc.cpp | 14 +-- 5 files changed, 200 insertions(+), 6 deletions(-) diff --git a/lib/burnside/ast-builder.cc b/lib/burnside/ast-builder.cc index db06e7457d32..5a4b26ceab12 100644 --- a/lib/burnside/ast-builder.cc +++ b/lib/burnside/ast-builder.cc @@ -602,6 +602,166 @@ inline void annotateFuncCFG(AST::FunctionLikeUnit &flu) { annotateEvalListCFG(flu.evals, nullptr); } +const char *evalName(AST::Evaluation &e) { + return std::visit( + Co::visitors{ + [](const Pa::AllocateStmt *) { return "AllocateStmt"; }, + [](const Pa::ArithmeticIfStmt *) { return "ArithmeticIfStmt"; }, + [](const Pa::AssignedGotoStmt *) { return "AssignedGotoStmt"; }, + [](const Pa::AssignmentStmt *x) { return "AssignmentStmt"; }, + [](const Pa::AssignStmt *) { return "AssignStmt"; }, + [](const Pa::BackspaceStmt *) { return "BackspaceStmt"; }, + [](const Pa::CallStmt *) { return "CallStmt"; }, + [](const Pa::CloseStmt *) { return "CloseStmt"; }, + [](const Pa::ComputedGotoStmt *) { return "ComputedGotoStmt"; }, + [](const Pa::ContinueStmt *) { return "ContinueStmt"; }, + [](const Pa::CycleStmt *) { return "CycleStmt"; }, + [](const Pa::DeallocateStmt *) { return "DeallocateStmt"; }, + [](const Pa::EndfileStmt *) { return "EndfileStmt"; }, + [](const Pa::EventPostStmt *) { return "EventPostStmt"; }, + [](const Pa::EventWaitStmt *) { return "EventWaitStmt"; }, + [](const Pa::ExitStmt *) { return "ExitStmt"; }, + [](const Pa::FailImageStmt *) { return "FailImageStmt"; }, + [](const Pa::FlushStmt *) { return "FlushStmt"; }, + [](const Pa::ForallStmt *) { return "ForallStmt"; }, + [](const Pa::FormTeamStmt *) { return "FormTeamStmt"; }, + [](const Pa::GotoStmt *) { return "GotoStmt"; }, + [](const Pa::IfStmt *x) { return "IfStmt"; }, + [](const Pa::InquireStmt *) { return "InquireStmt"; }, + [](const Pa::LockStmt *) { return "LockStmt"; }, + [](const Pa::NullifyStmt *) { return "NullifyStmt"; }, + [](const Pa::OpenStmt *) { return "OpenStmt"; }, + [](const Pa::PauseStmt *) { return "PauseStmt"; }, + [](const Pa::PointerAssignmentStmt *) { + return "PointerAssignmentStmt"; + }, + [](const Pa::PrintStmt *) { return "PrintStmt"; }, + [](const Pa::ReadStmt *) { return "ReadStmt"; }, + [](const Pa::ReturnStmt *) { return "ReturnStmt"; }, + [](const Pa::RewindStmt *) { return "RewindStmt"; }, + [](const Pa::StopStmt *) { return "StopStmt"; }, + [](const Pa::SyncAllStmt *) { return "SyncAllStmt"; }, + [](const Pa::SyncImagesStmt *) { return "SyncImagesStmt"; }, + [](const Pa::SyncMemoryStmt *) { return "SyncMemoryStmt"; }, + [](const Pa::SyncTeamStmt *) { return "SyncTeamStmt"; }, + [](const Pa::UnlockStmt *) { return "UnlockStmt"; }, + [](const Pa::WaitStmt *) { return "WaitStmt"; }, + [](const Pa::WhereStmt *) { return "WhereStmt"; }, + [](const Pa::WriteStmt *) { return "WriteStmt"; }, + + [](const AST::CGJump) { return "CGJump"; }, + + [](const Pa::DataStmt *) { return "DataStmt"; }, + [](const Pa::EntryStmt *) { return "EntryStmt"; }, + [](const Pa::FormatStmt *) { return "FormatStmt"; }, + [](const Pa::NamelistStmt *) { return "NamelistStmt"; }, + + [](const Pa::AssociateConstruct *) { return "AssociateConstruct"; }, + [](const Pa::BlockConstruct *) { return "BlockConstruct"; }, + [](const Pa::CaseConstruct *) { return "CaseConstruct"; }, + [](const Pa::ChangeTeamConstruct *) { return "ChangeTeamConstruct"; }, + [](const Pa::CompilerDirective *) { return "CompilerDirective"; }, + [](const Pa::CriticalConstruct *) { return "CriticalConstruct"; }, + [](const Pa::DoConstruct *) { return "DoConstruct"; }, + [](const Pa::ForallConstruct *) { return "ForallConstruct"; }, + [](const Pa::IfConstruct *) { return "IfConstruct"; }, + [](const Pa::OmpEndLoopDirective *) { return "OmpEndLoopDirective"; }, + [](const Pa::OpenMPConstruct *) { return "OpenMPConstruct"; }, + [](const Pa::SelectRankConstruct *) { return "SelectRankConstruct"; }, + [](const Pa::SelectTypeConstruct *) { return "SelectTypeConstruct"; }, + [](const Pa::WhereConstruct *) { return "WhereConstruct"; }, + + [](const Pa::AssociateStmt *) { return "AssociateStmt"; }, + [](const Pa::BlockStmt *) { return "BlockStmt"; }, + [](const Pa::CaseStmt *) { return "CaseStmt"; }, + [](const Pa::ChangeTeamStmt *) { return "ChangeTeamStmt"; }, + [](const Pa::CriticalStmt *) { return "CriticalStmt"; }, + [](const Pa::ElseIfStmt *x) { return "ElseIfStmt"; }, + [](const Pa::ElseStmt *) { return "ElseStmt"; }, + [](const Pa::ElsewhereStmt *) { return "ElsewhereStmt"; }, + [](const Pa::EndAssociateStmt *) { return "EndAssociateStmt"; }, + [](const Pa::EndBlockStmt *) { return "EndBlockStmt"; }, + [](const Pa::EndChangeTeamStmt *) { return "EndChangeTeamStmt"; }, + [](const Pa::EndCriticalStmt *) { return "EndCriticalStmt"; }, + [](const Pa::EndDoStmt *) { return "EndDoStmt"; }, + [](const Pa::EndForallStmt *) { return "EndForallStmt"; }, + [](const Pa::EndIfStmt *) { return "EndIfStmt"; }, + [](const Pa::EndSelectStmt *) { return "EndSelectStmt"; }, + [](const Pa::EndWhereStmt *) { return "EndWhereStmt"; }, + [](const Pa::ForallConstructStmt *) { return "ForallConstructStmt"; }, + [](const Pa::IfThenStmt *x) { return "IfThenStmt"; }, + [](const Pa::MaskedElsewhereStmt *) { return "MaskedElsewhereStmt"; }, + [](const Pa::NonLabelDoStmt *) { return "NonLabelDoStmt"; }, + [](const Pa::SelectCaseStmt *) { return "SelectCaseStmt"; }, + [](const Pa::SelectRankCaseStmt *) { return "SelectRankCaseStmt"; }, + [](const Pa::SelectRankStmt *) { return "SelectRankStmt"; }, + [](const Pa::SelectTypeStmt *) { return "SelectTypeStmt"; }, + [](const Pa::TypeGuardStmt *) { return "TypeGuardStmt"; }, + [](const Pa::WhereConstructStmt *) { return "WhereConstructStmt"; }, + }, + e.u); +} + +void dumpEvalList( + llvm::raw_ostream &o, std::list &evals, int indent = 1) { + static const std::string white{" ++"}; + std::string indentString{white.substr(0, indent * 2)}; + for (AST::Evaluation &e : evals) { + const char *name{evalName(e)}; + if (e.isConstruct()) { + o << indentString << "<<" << name << ">>\n"; + dumpEvalList(o, *e.getConstructEvals(), indent + 1); + o << indentString << "<>\n"; + } else { + o << indentString << name << ": " << e.pos.ToString() << '\n'; + } + } +} + +void dumpFunctionLikeUnit(llvm::raw_ostream &o, AST::FunctionLikeUnit &flu) { + const char *unitKind{}; + std::string name{}; + std::string header{}; + std::visit(Co::visitors{ + [&](const Pa::Statement *s) { + unitKind = "Program"; + name = s->statement.v.ToString(); + }, + [&](const Pa::Statement *s) { + unitKind = "Function"; + name = std::get(s->statement.t).ToString(); + header = s->source.ToString(); + }, + [&](const Pa::Statement *s) { + unitKind = "Subroutine"; + name = std::get(s->statement.t).ToString(); + header = s->source.ToString(); + }, + [&](const Pa::Statement *s) { + unitKind = "MpSubprogram"; + name = s->statement.v.ToString(); + header = s->source.ToString(); + }, + [&](auto *) { + if (std::get_if *>( + &flu.funStmts.back())) { + unitKind = "Program"; + name = ""; + } else { + unitKind = ">>>>> Error - no program unit <<<<<"; + } + }, + }, + flu.funStmts.front()); + o << unitKind << ' ' << name; + if (header.size()) { + o << ": " << header; + } + o << '\n'; + dumpEvalList(o, flu.evals); + o << "End" << unitKind << ' ' << name << "\n\n"; +} + } // namespace Br::AST::FunctionLikeUnit::FunctionLikeUnit( @@ -679,3 +839,25 @@ void Br::annotateControl(AST::Program &ast) { unit); } } + +/// Dump an AST. +void Br::dumpAST(llvm::raw_ostream &o, AST::Program &ast) { + for (auto &unit : ast.getUnits()) { + std::visit( + Co::visitors{ + [&](AST::BlockDataUnit &) { o << "BlockData\nEndBlockData\n\n"; }, + [&](AST::FunctionLikeUnit &f) { + dumpFunctionLikeUnit(o, f); + for (auto &f : f.funcs) { + dumpFunctionLikeUnit(o, f); + } + }, + [&](AST::ModuleLikeUnit &u) { + for (auto &f : u.funcs) { + dumpFunctionLikeUnit(o, f); + } + }, + }, + unit); + } +} diff --git a/lib/burnside/ast-builder.h b/lib/burnside/ast-builder.h index c639d6044cd1..a29afe61122d 100644 --- a/lib/burnside/ast-builder.h +++ b/lib/burnside/ast-builder.h @@ -17,6 +17,7 @@ #include "../parser/parse-tree.h" #include "../semantics/scope.h" +#include "llvm/Support/raw_ostream.h" namespace Fortran::burnside { namespace AST { @@ -324,6 +325,8 @@ AST::Program *createAST(const parser::Program &root); /// use in generating a CFG-like structure. void annotateControl(AST::Program &ast); +void dumpAST(llvm::raw_ostream &o, AST::Program &ast); + } // namespace burnside #endif // FORTRAN_BURNSIDE_AST_BUILDER_H_ diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 24fa8975492f..93341c54b6a3 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -1129,6 +1129,9 @@ void Br::BurnsideBridge::lower( const Pa::Program &prg, fir::NameUniquer &uniquer) { AST::Program *ast{Br::createAST(prg)}; Br::annotateControl(*ast); + if (getDumpPreFIR()) { + Br::dumpAST(llvm::errs(), *ast); + } FirConverter converter{*this, uniquer}; converter.run(*ast); delete ast; diff --git a/lib/burnside/bridge.h b/lib/burnside/bridge.h index 16763f262525..a354409f52af 100644 --- a/lib/burnside/bridge.h +++ b/lib/burnside/bridge.h @@ -136,6 +136,9 @@ class BurnsideBridge { /// Cross the bridge from the Fortran parse-tree, etc. to FIR+OpenMP+MLIR void lower(const parser::Program &program, fir::NameUniquer &uniquer); + void dumpPreFIR(bool flag = true) { dumpPreFIR_ = flag; } + bool getDumpPreFIR() { return dumpPreFIR_; } + private: explicit BurnsideBridge(const common::IntrinsicTypeDefaultKinds &defaultKinds, const parser::CookedSource *cooked); @@ -146,6 +149,7 @@ class BurnsideBridge { const parser::CookedSource *cooked; std::unique_ptr context; std::unique_ptr module; + bool dumpPreFIR_{false}; }; } // Fortran::burnside diff --git a/tools/bbc/bbc.cpp b/tools/bbc/bbc.cpp index 372ad88370de..c60d310b4741 100644 --- a/tools/bbc/bbc.cpp +++ b/tools/bbc/bbc.cpp @@ -115,11 +115,11 @@ struct DriverOptions { bool dumpUnparseWithSymbols{false}; bool dumpParseTree{false}; bool dumpSymbols{false}; - bool debugLinearFIR{false}; bool debugResolveNames{false}; bool debugSemantics{false}; bool measureTree{false}; bool runBackend{true}; + bool dumpPreFIR{false}; bool dumpHLFIR{true}; bool dumpFIR{true}; bool lowerToStd{true}; @@ -244,8 +244,7 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, } // TODO: Change this predicate to just "if (!driver.debugNoSemantics)" if (driver.debugSemantics || driver.debugResolveNames || driver.dumpSymbols || - driver.dumpUnparseWithSymbols || driver.debugLinearFIR || - driver.runBackend) { + driver.dumpUnparseWithSymbols || driver.runBackend) { Fortran::semantics::Semantics semantics{ semanticsContext, parseTree, parsing.cooked()}; semantics.Perform(); @@ -272,6 +271,9 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, fir::NameUniquer nameUniquer; auto burnside = Br::BurnsideBridge::create( semanticsContext.defaultKinds(), &parsing.cooked()); + if (driver.dumpPreFIR) { + burnside.dumpPreFIR(); + } burnside.lower(parseTree, nameUniquer); mlir::ModuleOp mlirModule{burnside.getModule()}; mlir::PassManager pm{mlirModule.getContext()}; @@ -444,8 +446,8 @@ int main(int argc, char *const argv[]) { driver.dumpUnparse = true; } else if (arg == "-funparse-with-symbols") { driver.dumpUnparseWithSymbols = true; - } else if (arg == "-fdebug-dump-linear-ir") { - driver.debugLinearFIR = true; + } else if (arg == "-fdebug-dump-pre-fir") { + driver.dumpPreFIR = true; } else if (arg == "-fno-dump-hl-fir") { driver.dumpHLFIR = false; } else if (arg == "-fno-dump-fir") { @@ -512,8 +514,8 @@ int main(int argc, char *const argv[]) { << " -fdebug-resolve-names\n" << " -fdebug-instrumented-parse\n" << " -fdebug-semantics perform semantic checks\n" + << " -fdebug-dump-pre-fir dump the IR tree prior to FIR\n" << " -fdotty print FIR as a dotty graph\n" - << " -fdebug-dump-linear-ir dump the flat linear FIR for debug\n" << " -v -c -o -I -D -U have their usual meanings\n" << " -help print this again\n" << "Other options are passed through to the compiler.\n"; From 064b7ab68b0b347d3a20da5fc575eb0e7f3584d0 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 17 Dec 2019 16:03:47 -0800 Subject: [PATCH 072/123] initial work on lowering select_type --- include/fir/FIROps.td | 8 ++ include/fir/FIROpsSupport.h | 7 ++ lib/fir/FIROps.cpp | 12 +++ lib/fir/StdConverter.cpp | 191 ++++++++++++++++++++---------------- tools/bbc/CMakeLists.txt | 1 + tools/bbc/bbc.cpp | 7 +- 6 files changed, 136 insertions(+), 90 deletions(-) diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index f127d78cd632..322082ee2aba 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -1900,6 +1900,14 @@ def fir_GenTypeDescOp : fir_OneResultOp<"gentypedesc", [NoSideEffect]> { return M::success(); }]; + let builders = [OpBuilder< + "Builder *, OperationState &result, mlir::TypeAttr res", + [{ + result.addAttribute("in_type", res); + result.addTypes(res.getValue()); + }] + >]; + let printer = [{ p << getOperationName() << ' ' << getAttr("in_type"); p << " : " << getType(); diff --git a/include/fir/FIROpsSupport.h b/include/fir/FIROpsSupport.h index a0f48de4c69e..34c1c1db4fdc 100644 --- a/include/fir/FIROpsSupport.h +++ b/include/fir/FIROpsSupport.h @@ -61,6 +61,13 @@ inline bool pureCall(mlir::Operation *op) { 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); + } // namespace fir #endif // FIR_FIROPS_SUPPORT_H diff --git a/lib/fir/FIROps.cpp b/lib/fir/FIROps.cpp index b3653c777c98..aafd99354c8a 100644 --- a/lib/fir/FIROps.cpp +++ b/lib/fir/FIROps.cpp @@ -14,9 +14,12 @@ #include "fir/FIROps.h" #include "fir/Attribute.h" +#include "fir/FIROpsSupport.h" #include "fir/FIRType.h" #include "mlir/Dialect/StandardOps/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" @@ -587,6 +590,15 @@ bool isReferenceLike(M::Type type) { type.isa(); } +M::FuncOp createFuncOp(M::Location loc, M::ModuleOp module, StringRef name, + M::FunctionType type) { + if (auto f = module.lookupSymbol(name)) + return f; + auto f = M::FuncOp::create(loc, name, type); + module.push_back(f); + return f; +} + // Tablegen operators #define GET_OP_CLASSES diff --git a/lib/fir/StdConverter.cpp b/lib/fir/StdConverter.cpp index e25fbda94baa..c98fabdf8870 100644 --- a/lib/fir/StdConverter.cpp +++ b/lib/fir/StdConverter.cpp @@ -13,25 +13,16 @@ // limitations under the License. #include "fir/Transforms/StdConverter.h" +#include "fir/Attribute.h" #include "fir/FIRDialect.h" -#include "fir/FIROps.h" +#include "fir/FIROpsSupport.h" #include "fir/FIRType.h" #include "mlir/Conversion/AffineToStandard/AffineToStandard.h" -#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" -#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h" -#include "mlir/Dialect/AffineOps/AffineOps.h" -#include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/StandardOps/Ops.h" #include "mlir/IR/StandardTypes.h" #include "mlir/Pass/Pass.h" -#include "mlir/Target/LLVMIR.h" +#include "mlir/Transforms/DialectConversion.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/Config/abi-breaking.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Type.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/raw_ostream.h" // This module performs the conversion of FIR operations to MLIR standard and/or // LLVM-IR dialects. @@ -59,6 +50,7 @@ class FIRToStdTypeConverter : public M::TypeConverter { using TypeConverter::TypeConverter; // convert a front-end kind value to either a std dialect type + // FIXME: use KindMapping static M::Type kindToRealType(M::MLIRContext *ctx, KindTy kind) { switch (kind) { case 2: @@ -75,109 +67,136 @@ class FIRToStdTypeConverter : public M::TypeConverter { /// Convert FIR types to MLIR standard dialect types M::Type convertType(M::Type t) override { - if (auto cplx = t.dyn_cast()) { + if (auto cplx = t.dyn_cast()) return M::ComplexType::get( kindToRealType(cplx.getContext(), cplx.getFKind())); - } - if (auto integer = t.dyn_cast()) { + if (auto integer = t.dyn_cast()) return M::IntegerType::get(integer.getFKind() * 8, integer.getContext()); - } - if (auto real = t.dyn_cast()) { + if (auto real = t.dyn_cast()) return kindToRealType(real.getContext(), real.getFKind()); - } return t; } }; -// Lower a SELECT operation into a cascade of conditional branches. The last -// case must be the `true` condition. -inline void rewriteSelectConstruct(M::Operation *op, OperandTy operands, - L::ArrayRef dests, - L::ArrayRef destOperands, - M::OpBuilder &rewriter) { - L::SmallVector noargs; - L::SmallVector blocks; - auto loc{op->getLoc()}; - blocks.push_back(rewriter.getInsertionBlock()); - for (std::size_t i = 1; i < dests.size(); ++i) - blocks.push_back(rewriter.createBlock(dests[0])); - rewriter.setInsertionPointToEnd(blocks[0]); - if (dests.size() == 1) { - rewriter.create(loc, dests[0], destOperands[0]); - return; - } - rewriter.create(loc, operands[1], dests[0], destOperands[0], - blocks[1], noargs); - for (std::size_t i = 1; i < dests.size() - 1; ++i) { - rewriter.setInsertionPointToEnd(blocks[i]); - rewriter.create(loc, operands[i + 1], dests[i], - destOperands[i], blocks[i + 1], noargs); - } - std::size_t last{dests.size() - 1}; - rewriter.setInsertionPointToEnd(blocks[last]); - rewriter.create(loc, dests[last], destOperands[last]); -} +/// FIR conversion pattern template +template +class FIROpConversion : public M::ConversionPattern { +public: + explicit FIROpConversion(M::MLIRContext *ctx, FIRToStdTypeConverter &lowering) + : ConversionPattern(FromOp::getOperationName(), 1, ctx), + lowering(lowering) {} + +protected: + M::Type convertType(M::Type ty) const { return lowering.convertType(ty); } + + FIRToStdTypeConverter &lowering; +}; -/// Convert FIR dialect to standard dialect -class FIRToStdLoweringPass : public M::ModulePass { - M::OpBuilder *builder; - - void lowerSelect(M::Operation *op) { - if (M::dyn_cast(op) || M::dyn_cast(op) || - M::dyn_cast(op)) { - // build the lists of operands and successors - L::SmallVector operands{op->operand_begin(), - op->operand_end()}; - L::SmallVector destinations; - destinations.reserve(op->getNumSuccessors()); - L::SmallVector destOperands; - unsigned firstSuccOpd = op->getSuccessorOperandIndex(0); - for (unsigned i = 0, seen = 0, e = op->getNumSuccessors(); i < e; ++i) { - destinations.push_back(op->getSuccessor(i)); - unsigned n = op->getNumSuccessorOperands(i); - destOperands.push_back( - L::makeArrayRef(operands.data() + firstSuccOpd + seen, n)); - seen += n; +/// SelectTypeOp converted to an if-then-else chain +/// +/// This lowers the test conditions to calls into the runtime +struct SelectTypeOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + M::PatternMatchResult + matchAndRewrite(M::Operation *op, OperandTy operands, + L::ArrayRef destinations, + L::ArrayRef destOperands, + M::ConversionPatternRewriter &rewriter) const override { + auto selecttype = M::cast(op); + auto conds = selecttype.getNumConditions(); + auto attrName = SelectTypeOp::AttrName; + auto caseAttr = selecttype.getAttrOfType(attrName); + auto cases = caseAttr.getValue(); + // Selector must be of type !fir.box + auto &selector = operands[0]; + auto loc = selecttype.getLoc(); + auto mod = op->getParentOfType(); + for (unsigned t = 0; t != conds; ++t) { + auto &attr = cases[t]; + if (auto a = attr.dyn_cast_or_null()) { + genTypeLadderStep(loc, true, selector, a.getType(), destinations[t], + destOperands[t], mod, rewriter); + continue; } - // do the rewrite - rewriteSelectConstruct( - op, L::makeArrayRef(operands.data(), operands.data() + firstSuccOpd), - destinations, destOperands, *builder); + if (auto a = attr.dyn_cast_or_null()) { + genTypeLadderStep(loc, false, selector, a.getType(), destinations[t], + destOperands[t], mod, rewriter); + continue; + } + assert(attr.dyn_cast_or_null()); + assert((t + 1 == conds) && "unit must be last"); + rewriter.replaceOpWithNewOp(selecttype, destinations[t], + M::ValueRange{destOperands[t]}); } + return matchSuccess(); + } + + static void lookupFunction(L::StringRef name, M::FunctionType type, + M::ModuleOp module, + M::ConversionPatternRewriter &rewriter) { + fir::createFuncOp(rewriter.getUnknownLoc(), module, name, type); + } + + static void genTypeLadderStep(M::Location loc, bool exactTest, + M::Value *selector, M::Type ty, M::Block *dest, + OperandTy destOps, M::ModuleOp module, + M::ConversionPatternRewriter &rewriter) { + M::Type tydesc = fir::TypeDescType::get(ty); + M::Value *t = rewriter.create(loc, M::TypeAttr::get(tydesc)); + std::vector actuals = {selector, t}; + auto fty = rewriter.getI1Type(); + std::vector argTy = {fir::BoxType::get(rewriter.getNoneType()), + tydesc}; + L::StringRef funName = + exactTest ? "FIXME_exact_type_match" : "FIXME_isa_type_test"; + lookupFunction(funName, rewriter.getFunctionType(argTy, fty), module, + rewriter); + // FIXME: need to call actual runtime routines for (1) testing if the + // runtime type of the selector is an exact match to a derived type or (2) + // testing if the runtime type of the selector is a derived type or one of + // that derived type's subtypes. + auto cmp = rewriter.create( + loc, fty, rewriter.getSymbolRefAttr(funName), actuals); + auto *thisBlock = rewriter.getInsertionBlock(); + auto *newBlock = rewriter.createBlock(dest); + rewriter.setInsertionPointToEnd(thisBlock); + rewriter.create(loc, cmp.getResult(0), dest, destOps, + newBlock, OperandTy{}); + rewriter.setInsertionPointToEnd(newBlock); } +}; +/// Convert affine dialect, fir.select_type to standard dialect +class FIRToStdLoweringPass : public M::FunctionPass { public: - void runOnModule() override { + void runOnFunction() override { if (ClDisableFirToStd) return; - return; // FIXME - - for (auto fn : getModule().getOps()) { - M::OpBuilder rewriter{&fn.getBody()}; - builder = &rewriter; - fn.walk([&](M::Operation *op) { lowerSelect(op); }); - } - auto &context{getContext()}; + auto *context{&getContext()}; FIRToStdTypeConverter typeConverter; M::OwningRewritePatternList patterns; - // patterns.insert<>(&context, typeConverter); - M::populateAffineToStdConversionPatterns(patterns, &context); - M::populateFuncOpTypeConversionPattern(patterns, &context, typeConverter); - M::ConversionTarget target{context}; + patterns.insert(context, typeConverter); + M::populateAffineToStdConversionPatterns(patterns, context); + M::populateFuncOpTypeConversionPattern(patterns, context, typeConverter); + M::ConversionTarget target{*context}; target.addLegalDialect(); target.addDynamicallyLegalOp([&](M::FuncOp op) { return typeConverter.isSignatureLegal(op.getType()); }); - target.addDynamicallyLegalOp( - [&](M::ModuleOp op) { return true; }); + target.addIllegalOp(); if (M::failed(M::applyPartialConversion( getModule(), target, std::move(patterns), &typeConverter))) { - M::emitError(M::UnknownLoc::get(&context), + M::emitError(M::UnknownLoc::get(context), "error in converting to standard dialect\n"); signalPassFailure(); } } + + M::ModuleOp getModule() { + return getFunction().getParentOfType(); + } }; } // namespace diff --git a/tools/bbc/CMakeLists.txt b/tools/bbc/CMakeLists.txt index 36f351529fd1..f0917485a173 100644 --- a/tools/bbc/CMakeLists.txt +++ b/tools/bbc/CMakeLists.txt @@ -27,6 +27,7 @@ target_link_libraries(FirBbcLib ${LIBS} FIRTransforms) set(EXE_LIBS ${LIBS} FIRTransforms + MLIRAffineToStandard FortranCommon FortranParser FortranEvaluate diff --git a/tools/bbc/bbc.cpp b/tools/bbc/bbc.cpp index c60d310b4741..78402849a5aa 100644 --- a/tools/bbc/bbc.cpp +++ b/tools/bbc/bbc.cpp @@ -284,12 +284,11 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, // Run FIR mem2reg and CSE as a pair pm.addPass(fir::createMemToRegPass()); pm.addPass(fir::createCSEPass()); - if (driver.lowerToStd) { - pm.addPass(fir::createFIRToStdPass()); - } if (driver.lowerToLLVM) { pm.addPass(fir::createLowerToLoopPass()); - pm.addPass(fir::createFIRToStdPass()); + if (driver.lowerToStd) { + pm.addPass(fir::createFIRToStdPass()); + } pm.addPass(mlir::createLowerToCFGPass()); pm.addPass(fir::createFIRToLLVMPass(nameUniquer)); } From 4e19c7906822d927ce66157e691857277e1a403f Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Wed, 18 Dec 2019 13:25:33 -0800 Subject: [PATCH 073/123] Split assignment lowering into different cases --- lib/burnside/bridge.cc | 51 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 93341c54b6a3..ad8cafb567b1 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -676,10 +676,53 @@ class FirConverter : public AbstractConverter { void genFIR(const Pa::AllocateStmt &) { TODO(); } void genFIR(const Pa::AssignmentStmt &stmt) { - auto *rhs{Se::GetExpr(std::get(stmt.t))}; - auto *lhs{Se::GetExpr(std::get(stmt.t))}; - builder->create( - toLocation(), genExprValue(*rhs), genExprAddr(*lhs)); + assert(stmt.typedAssignment && "assignment analysis failed"); + // Warning: v->u must become v.u after next f18 rebase + if (auto *assignment{std::get_if( + &stmt.typedAssignment->v->u)}) { + const Se::Symbol *sym{Ev::UnwrapWholeSymbolDataRef(assignment->lhs)}; + if (sym && Se::IsAllocatable(*sym)) { + // Assignment of allocatable are more complex, the lhs + // may need to be deallocated/reallocated. + // See Fortran 2018 10.2.1.3 p3 + TODO(); + } else if (sym && Se::IsPointer(*sym)) { + // Target of the pointer must be assigned. + // See Fortran 2018 10.2.1.3 p2 + TODO(); + } else if (assignment->lhs.Rank() > 0) { + // Array assignment + // See Fortran 2018 10.2.1.3 p5, p6, and p7 + TODO(); + } else { + // Scalar assignments + std::optional lhsType{assignment->lhs.GetType()}; + assert(lhsType && "lhs cannot be typeless"); + switch (lhsType->category()) { + case IntegerCat: + case RealCat: + case ComplexCat: + case LogicalCat: + // Fortran 2018 10.2.1.3 p8 and p9 + // Conversions are already inserted by semantic + // analysis. + builder->create(toLocation(), + genExprValue(assignment->rhs), genExprAddr(assignment->lhs)); + break; + case CharacterCat: + // Fortran 2018 10.2.1.3 p10 and p11 + TODO(); + break; + case DerivedCat: + // Fortran 2018 10.2.1.3 p12 and p13 + TODO(); + break; + } + } + } else { + // Defined assignment: call ProcRef + TODO(); + } } void genFIR(const Pa::ContinueStmt &) {} // do nothing From c09d2b8d72e1df974dfbee28acb0fb82baa02232 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Wed, 18 Dec 2019 14:57:59 -0800 Subject: [PATCH 074/123] tweaks to FuncOp getter --- include/fir/FIROpsSupport.h | 4 ++++ lib/burnside/builder.cc | 12 +++++------- test/fir/select-type.fir | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 test/fir/select-type.fir diff --git a/include/fir/FIROpsSupport.h b/include/fir/FIROpsSupport.h index 34c1c1db4fdc..6a133045975d 100644 --- a/include/fir/FIROpsSupport.h +++ b/include/fir/FIROpsSupport.h @@ -68,6 +68,10 @@ inline bool pureCall(mlir::Operation *op) { mlir::FuncOp createFuncOp(mlir::Location loc, mlir::ModuleOp module, llvm::StringRef name, mlir::FunctionType type); +/// Get or create a GlobalOp in a module. +fir::GlobalOp createGlobalOp(mlir::Location loc, mlir::ModuleOp module, + llvm::StringRef name, mlir::Type type); + } // namespace fir #endif // FIR_FIROPS_SUPPORT_H diff --git a/lib/burnside/builder.cc b/lib/burnside/builder.cc index 9f2b834b95f7..b523ffaf097b 100644 --- a/lib/burnside/builder.cc +++ b/lib/burnside/builder.cc @@ -15,6 +15,7 @@ #include "builder.h" #include "bridge.h" #include "convert-type.h" +#include "fir/FIROpsSupport.h" #include "llvm/ADT/StringRef.h" #include "mlir/IR/Module.h" #include "mlir/IR/Value.h" @@ -29,17 +30,14 @@ using namespace Fortran::burnside; M::FuncOp B::createFunction(B::AbstractConverter &converter, llvm::StringRef name, M::FunctionType funcTy) { - auto func{M::FuncOp::create(converter.getCurrentLocation(), name, funcTy)}; - converter.getModuleOp().push_back(func); - return func; + return fir::createFuncOp( + converter.getCurrentLocation(), converter.getModuleOp(), name, funcTy); } M::FuncOp B::createFunction( M::ModuleOp module, llvm::StringRef name, M::FunctionType funcTy) { - auto loc{M::UnknownLoc::get(module.getContext())}; - auto func{M::FuncOp::create(loc, name, funcTy)}; - module.push_back(func); - return func; + return fir::createFuncOp( + M::UnknownLoc::get(module.getContext()), module, name, funcTy); } M::FuncOp B::getNamedFunction(M::ModuleOp module, llvm::StringRef name) { diff --git a/test/fir/select-type.fir b/test/fir/select-type.fir new file mode 100644 index 000000000000..5c24bc01578b --- /dev/null +++ b/test/fir/select-type.fir @@ -0,0 +1,14 @@ +func @f(%a : !fir.box) -> i32 { + %1 = constant 4 : i32 + %2 = constant 8 : i32 + %3 = constant 16 : i32 + fir.select_type %a : !fir.box [#fir.instance>, ^bb2(%1:i32), #fir.subsumed>, ^bb3(%3:i32), unit, ^bb4(%2:i32)] +^bb2(%4 : i32) : + return %4 : i32 +^bb3(%5 : i32) : + %6 = addi %5, %5 : i32 + return %6 : i32 +^bb4(%7 : i32) : + %8 = muli %7, %7 : i32 + return %8 : i32 +} From d36fc65ef4a4df88053df1daf14bc03e0eab2b00 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Wed, 18 Dec 2019 16:22:49 -0800 Subject: [PATCH 075/123] thread KindMapping through the eyes of the needles --- include/fir/KindMapping.h | 2 + include/fir/Transforms/StdConverter.h | 4 +- lib/fir/StdConverter.cpp | 57 ++++++++++++++++----------- tools/bbc/bbc.cpp | 4 +- tools/tco/tco.cpp | 4 +- 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/include/fir/KindMapping.h b/include/fir/KindMapping.h index f5e3ca7cc504..b6d2caa96570 100644 --- a/include/fir/KindMapping.h +++ b/include/fir/KindMapping.h @@ -54,6 +54,8 @@ class KindMapping { /// Get the LLVM Type::TypeID of !fir.complex LLVMTypeID getComplexTypeID(KindTy kind); + mlir::MLIRContext *getContext() const { return context; } + private: MatchResult badMapString(llvm::Twine const &ptr); MatchResult parse(llvm::StringRef kindMap); diff --git a/include/fir/Transforms/StdConverter.h b/include/fir/Transforms/StdConverter.h index 80ef0aca2615..b0ac1fa7aa80 100644 --- a/include/fir/Transforms/StdConverter.h +++ b/include/fir/Transforms/StdConverter.h @@ -23,8 +23,10 @@ class Pass; namespace fir { +class KindMapping; + /// Convert FIR to the standard dialect -std::unique_ptr createFIRToStdPass(); +std::unique_ptr createFIRToStdPass(KindMapping &); } // namespace fir diff --git a/lib/fir/StdConverter.cpp b/lib/fir/StdConverter.cpp index c98fabdf8870..a0684a131c82 100644 --- a/lib/fir/StdConverter.cpp +++ b/lib/fir/StdConverter.cpp @@ -17,6 +17,7 @@ #include "fir/FIRDialect.h" #include "fir/FIROpsSupport.h" #include "fir/FIRType.h" +#include "fir/KindMapping.h" #include "mlir/Conversion/AffineToStandard/AffineToStandard.h" #include "mlir/Dialect/StandardOps/Ops.h" #include "mlir/IR/StandardTypes.h" @@ -49,33 +50,42 @@ class FIRToStdTypeConverter : public M::TypeConverter { public: using TypeConverter::TypeConverter; - // convert a front-end kind value to either a std dialect type - // FIXME: use KindMapping - static M::Type kindToRealType(M::MLIRContext *ctx, KindTy kind) { - switch (kind) { - case 2: + explicit FIRToStdTypeConverter(KindMapping &kindMap) : kindMap{kindMap} {} + + // convert front-end REAL kind value to a std dialect type, if possible + static M::Type kindToRealType(KindMapping &kindMap, KindTy kind) { + auto *ctx = kindMap.getContext(); + switch (kindMap.getRealTypeID(kind)) { + case L::Type::TypeID::HalfTyID: return M::FloatType::getF16(ctx); - case 3: +#if 0 + case L::Type::TypeID:: FIXME TyID: return M::FloatType::getBF16(ctx); - case 4: +#endif + case L::Type::TypeID::FloatTyID: return M::FloatType::getF32(ctx); - case 8: + case L::Type::TypeID::DoubleTyID: return M::FloatType::getF64(ctx); + case L::Type::TypeID::X86_FP80TyID: // MLIR does not support yet + case L::Type::TypeID::FP128TyID: // MLIR does not support yet + default: + return fir::RealType::get(ctx, kind); } - return fir::RealType::get(ctx, kind); } - /// Convert FIR types to MLIR standard dialect types + /// Convert some FIR types to MLIR standard dialect types M::Type convertType(M::Type t) override { if (auto cplx = t.dyn_cast()) - return M::ComplexType::get( - kindToRealType(cplx.getContext(), cplx.getFKind())); + return M::ComplexType::get(kindToRealType(kindMap, cplx.getFKind())); if (auto integer = t.dyn_cast()) return M::IntegerType::get(integer.getFKind() * 8, integer.getContext()); if (auto real = t.dyn_cast()) - return kindToRealType(real.getContext(), real.getFKind()); + return kindToRealType(kindMap, real.getFKind()); return t; } + +private: + KindMapping &kindMap; }; /// FIR conversion pattern template @@ -132,12 +142,6 @@ struct SelectTypeOpConversion : public FIROpConversion { return matchSuccess(); } - static void lookupFunction(L::StringRef name, M::FunctionType type, - M::ModuleOp module, - M::ConversionPatternRewriter &rewriter) { - fir::createFuncOp(rewriter.getUnknownLoc(), module, name, type); - } - static void genTypeLadderStep(M::Location loc, bool exactTest, M::Value *selector, M::Type ty, M::Block *dest, OperandTy destOps, M::ModuleOp module, @@ -150,8 +154,8 @@ struct SelectTypeOpConversion : public FIROpConversion { tydesc}; L::StringRef funName = exactTest ? "FIXME_exact_type_match" : "FIXME_isa_type_test"; - lookupFunction(funName, rewriter.getFunctionType(argTy, fty), module, - rewriter); + createFuncOp(rewriter.getUnknownLoc(), module, funName, + rewriter.getFunctionType(argTy, fty)); // FIXME: need to call actual runtime routines for (1) testing if the // runtime type of the selector is an exact match to a derived type or (2) // testing if the runtime type of the selector is a derived type or one of @@ -170,12 +174,14 @@ struct SelectTypeOpConversion : public FIROpConversion { /// Convert affine dialect, fir.select_type to standard dialect class FIRToStdLoweringPass : public M::FunctionPass { public: + explicit FIRToStdLoweringPass(KindMapping &kindMap) : kindMap{kindMap} {} + void runOnFunction() override { if (ClDisableFirToStd) return; auto *context{&getContext()}; - FIRToStdTypeConverter typeConverter; + FIRToStdTypeConverter typeConverter{kindMap}; M::OwningRewritePatternList patterns; patterns.insert(context, typeConverter); M::populateAffineToStdConversionPatterns(patterns, context); @@ -197,10 +203,13 @@ class FIRToStdLoweringPass : public M::FunctionPass { M::ModuleOp getModule() { return getFunction().getParentOfType(); } + +private: + KindMapping &kindMap; }; } // namespace -std::unique_ptr fir::createFIRToStdPass() { - return std::make_unique(); +std::unique_ptr fir::createFIRToStdPass(fir::KindMapping &kindMap) { + return std::make_unique(kindMap); } diff --git a/tools/bbc/bbc.cpp b/tools/bbc/bbc.cpp index 78402849a5aa..b1695c7363d9 100644 --- a/tools/bbc/bbc.cpp +++ b/tools/bbc/bbc.cpp @@ -31,6 +31,7 @@ #include "../../lib/semantics/unparse-with-symbols.h" #include "fir/FIRDialect.h" #include "fir/InternalNames.h" +#include "fir/KindMapping.h" #include "fir/Tilikum/Tilikum.h" #include "fir/Transforms/Passes.h" #include "fir/Transforms/StdConverter.h" @@ -271,6 +272,7 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, fir::NameUniquer nameUniquer; auto burnside = Br::BurnsideBridge::create( semanticsContext.defaultKinds(), &parsing.cooked()); + fir::KindMapping kindMap{&burnside.getMLIRContext()}; if (driver.dumpPreFIR) { burnside.dumpPreFIR(); } @@ -287,7 +289,7 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, if (driver.lowerToLLVM) { pm.addPass(fir::createLowerToLoopPass()); if (driver.lowerToStd) { - pm.addPass(fir::createFIRToStdPass()); + pm.addPass(fir::createFIRToStdPass(kindMap)); } pm.addPass(mlir::createLowerToCFGPass()); pm.addPass(fir::createFIRToLLVMPass(nameUniquer)); diff --git a/tools/tco/tco.cpp b/tools/tco/tco.cpp index b51b0be2481d..09ee4d82ca28 100644 --- a/tools/tco/tco.cpp +++ b/tools/tco/tco.cpp @@ -13,6 +13,7 @@ #include "fir/FIRDialect.h" #include "fir/InternalNames.h" +#include "fir/KindMapping.h" #include "fir/Tilikum/Tilikum.h" #include "fir/Transforms/Passes.h" #include "fir/Transforms/StdConverter.h" @@ -68,6 +69,7 @@ int compileFIR() { // run passes fir::NameUniquer uniquer; + fir::KindMapping kindMap{context.get()}; mlir::PassManager pm{context.get()}; pm.addPass(fir::createMemToRegPass()); pm.addPass(fir::createCSEPass()); @@ -75,7 +77,7 @@ int compileFIR() { pm.addPass(fir::createPromoteToAffinePass()); // convert fir dialect to loop pm.addPass(fir::createLowerToLoopPass()); - pm.addPass(fir::createFIRToStdPass()); + pm.addPass(fir::createFIRToStdPass(kindMap)); // convert loop dialect to standard pm.addPass(mlir::createLowerToCFGPass()); pm.addPass(fir::createFIRToLLVMPass(uniquer)); From 76dc163a115ebd2449e2584591fa66ba42f36bc4 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 19 Dec 2019 11:17:02 -0800 Subject: [PATCH 076/123] correct the plumbing of unboxchar and unboxproc --- lib/fir/Tilikum.cpp | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index 38f9643f4c7e..2e360deb58b4 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -1513,9 +1513,9 @@ void selectMatchAndRewrite(FIRToLLVMTypeConverter &lowering, M::Operation *op, } assert(attr.template dyn_cast_or_null()); assert((t + 1 == conds) && "unit must be last"); - rewriter.replaceOpWithNewOp( - select, M::ValueRange{}, destinations[t], - M::ValueRange{destOperands[t]}); + rewriter.replaceOpWithNewOp(select, M::ValueRange{}, + destinations[t], + M::ValueRange{destOperands[t]}); } } @@ -1586,8 +1586,19 @@ struct UnboxCharOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto unboxchar = M::cast(op); - unboxchar.replaceAllUsesWith(operands); - rewriter.replaceOp(unboxchar, {}); + auto ctx = unboxchar.getContext(); + auto loc = unboxchar.getLoc(); + auto *tuple = operands[0]; + auto ty = unwrap(tuple->getType()); + auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); + auto ptrty = ty.getStructElementType(0); + auto ptr = rewriter.create(loc, ptrty, tuple, c0); + auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); + auto lenty = ty.getStructElementType(1); + auto len = rewriter.create(loc, lenty, tuple, c1); + std::vector repls = {ptr, len}; + unboxchar.replaceAllUsesWith(repls); + rewriter.eraseOp(unboxchar); return matchSuccess(); } }; @@ -1600,6 +1611,7 @@ struct UnboxOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto unbox = M::cast(op); + // FIXME: thread all the tuple args to their uses here unbox.replaceAllUsesWith(operands); rewriter.replaceOp(unbox, {}); return matchSuccess(); @@ -1614,8 +1626,19 @@ struct UnboxProcOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto unboxproc = M::cast(op); - unboxproc.replaceAllUsesWith(operands); - rewriter.replaceOp(unboxproc, {}); + auto ctx = unboxproc.getContext(); + auto loc = unboxproc.getLoc(); + auto *tuple = operands[0]; + auto ty = unwrap(tuple->getType()); + auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); + auto ptrty = ty.getStructElementType(0); + auto ptr = rewriter.create(loc, ptrty, tuple, c0); + auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); + auto hty = ty.getStructElementType(1); + auto host = rewriter.create(loc, hty, tuple, c1); + std::vector repls = {ptr, host}; + unboxproc.replaceAllUsesWith(repls); + rewriter.eraseOp(unboxproc); return matchSuccess(); } }; From fc67d4c71e330ff7c2c02bf7b3cdb962565aa2e7 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 19 Dec 2019 12:33:10 -0800 Subject: [PATCH 077/123] work on unbox --- lib/fir/Tilikum.cpp | 66 ++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index 2e360deb58b4..e426b108a74b 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -1578,6 +1578,16 @@ struct StoreOpConversion : public FIROpConversion { } }; +// cons an extractvalue on a tuple value, returning value at element `x` +M::LLVM::ExtractValueOp +genExtractValueWithIndex(M::Location loc, M::Value *tuple, M::LLVM::LLVMType ty, + M::ConversionPatternRewriter &rewriter, + M::MLIRContext *ctx, int x) { + auto cx = M::ArrayAttr::get(rewriter.getI32IntegerAttr(x), ctx); + auto xty = ty.getStructElementType(x); + return rewriter.create(loc, xty, tuple, cx); +} + // unbox a CHARACTER box value, yielding its components struct UnboxCharOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -1586,16 +1596,12 @@ struct UnboxCharOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto unboxchar = M::cast(op); - auto ctx = unboxchar.getContext(); + auto *ctx = unboxchar.getContext(); auto loc = unboxchar.getLoc(); auto *tuple = operands[0]; auto ty = unwrap(tuple->getType()); - auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); - auto ptrty = ty.getStructElementType(0); - auto ptr = rewriter.create(loc, ptrty, tuple, c0); - auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); - auto lenty = ty.getStructElementType(1); - auto len = rewriter.create(loc, lenty, tuple, c1); + auto ptr = genExtractValueWithIndex(loc, tuple, ty, rewriter, ctx, 0); + auto len = genExtractValueWithIndex(loc, tuple, ty, rewriter, ctx, 1); std::vector repls = {ptr, len}; unboxchar.replaceAllUsesWith(repls); rewriter.eraseOp(unboxchar); @@ -1603,7 +1609,20 @@ struct UnboxCharOpConversion : public FIROpConversion { } }; -// unbox a generic box value, yielding its components +// generate a GEP into a structure and load the element at position `x` +M::LLVM::LoadOp genLoadWithIndex(M::Location loc, M::Value *tuple, + M::LLVM::LLVMType ty, + M::ConversionPatternRewriter &rewriter, + M::MLIRContext *ctx, M::LLVM::LLVMType oty, + M::LLVM::ConstantOp c0, int x) { + auto ax = rewriter.getI32IntegerAttr(x); + auto cx = rewriter.create(loc, oty, ax); + auto xty = ty.getStructElementType(x); + auto gep = genGEP(loc, xty.getPointerTo(), rewriter, tuple, c0, cx); + return rewriter.create(loc, xty, gep); +} + +// unbox a generic box reference, yielding its components struct UnboxOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -1611,9 +1630,24 @@ struct UnboxOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto unbox = M::cast(op); - // FIXME: thread all the tuple args to their uses here - unbox.replaceAllUsesWith(operands); - rewriter.replaceOp(unbox, {}); + auto *ctx = unbox.getContext(); + auto loc = unbox.getLoc(); + auto *tuple = operands[0]; + auto ty = unwrap(tuple->getType()); + auto oty = lowering.offsetType(); + auto c0 = rewriter.create( + loc, oty, rewriter.getI32IntegerAttr(0)); + auto ptr = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 0); + auto len = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 1); + auto ver = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 2); + auto rank = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 3); + auto type = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 4); + auto attr = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 5); + auto xtra = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 6); + // FIXME: add dims, etc. + std::vector repls = {ptr, len, ver, rank, type, attr, xtra}; + unbox.replaceAllUsesWith(repls); + rewriter.eraseOp(unbox); return matchSuccess(); } }; @@ -1626,16 +1660,12 @@ struct UnboxProcOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto unboxproc = M::cast(op); - auto ctx = unboxproc.getContext(); + auto *ctx = unboxproc.getContext(); auto loc = unboxproc.getLoc(); auto *tuple = operands[0]; auto ty = unwrap(tuple->getType()); - auto c0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctx); - auto ptrty = ty.getStructElementType(0); - auto ptr = rewriter.create(loc, ptrty, tuple, c0); - auto c1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctx); - auto hty = ty.getStructElementType(1); - auto host = rewriter.create(loc, hty, tuple, c1); + auto ptr = genExtractValueWithIndex(loc, tuple, ty, rewriter, ctx, 0); + auto host = genExtractValueWithIndex(loc, tuple, ty, rewriter, ctx, 1); std::vector repls = {ptr, host}; unboxproc.replaceAllUsesWith(repls); rewriter.eraseOp(unboxproc); From ae536c0ee6bc3df30ca9a3d856038dcbeb11a79b Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 19 Dec 2019 13:27:18 -0800 Subject: [PATCH 078/123] add notes on kind mapping --- include/fir/KindMapping.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/include/fir/KindMapping.h b/include/fir/KindMapping.h index b6d2caa96570..e747c484a8b9 100644 --- a/include/fir/KindMapping.h +++ b/include/fir/KindMapping.h @@ -29,6 +29,27 @@ class MLIRContext; 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; From b07c8b7f25a548f13f0f0fad41fd519a176ece72 Mon Sep 17 00:00:00 2001 From: V Donaldson Date: Thu, 19 Dec 2019 16:11:42 -0800 Subject: [PATCH 079/123] Address review comments in prior commit; use more name shortcuts. --- lib/burnside/ast-builder.cc | 81 +++++++++++++++++++------------------ lib/burnside/bridge.cc | 75 +++++++++++++++++----------------- lib/burnside/bridge.h | 4 -- tools/bbc/bbc.cpp | 7 ---- 4 files changed, 80 insertions(+), 87 deletions(-) diff --git a/lib/burnside/ast-builder.cc b/lib/burnside/ast-builder.cc index 5a4b26ceab12..21a4e236c0bb 100644 --- a/lib/burnside/ast-builder.cc +++ b/lib/burnside/ast-builder.cc @@ -17,7 +17,7 @@ #include #include -/// Build an light-weight AST to help with lowering to FIR. The AST will +/// Build a light-weight AST to help with lowering to FIR. The AST will /// capture pointers back into the parse tree, so the parse tree data structure /// may not be changed between the construction of the AST and all of /// its uses. @@ -29,6 +29,7 @@ namespace Br = Fortran::burnside; namespace Co = Fortran::common; +namespace L = llvm; namespace Pa = Fortran::parser; using namespace Fortran; @@ -111,96 +112,96 @@ class ASTBuilder { // Construct statements // - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(makeEvalDirect(s)); } // Get rid of production wrapper - void Post(const Pa::UnlabeledStatement &s) { + void Post(const Pa::UnlabeledStatement &s) { addEval(std::visit( [&](const auto &x) { return AST::Evaluation{x, s.source, {}, parents.back()}; }, s.statement.u)); } - void Post(const Pa::Statement &s) { + void Post(const Pa::Statement &s) { addEval(std::visit( [&](const auto &x) { return AST::Evaluation{x, s.source, s.label, parents.back()}; @@ -248,7 +249,7 @@ class ASTBuilder { // the AST. AST::Evaluation makeEvalAction(const Pa::Statement &s) { return std::visit( - common::visitors{ + Co::visitors{ [&](const Pa::ContinueStmt &x) { return AST::Evaluation{x, s.source, s.label, parents.back()}; }, @@ -265,7 +266,7 @@ class ASTBuilder { AST::Evaluation makeEvalAction( const Pa::UnlabeledStatement &s) { return std::visit( - common::visitors{ + Co::visitors{ [&](const Pa::ContinueStmt &x) { return AST::Evaluation{x, s.source, {}, parents.back()}; }, @@ -485,7 +486,7 @@ void annotateEvalListCFG( e.isTarget = true; } std::visit( - common::visitors{ + Co::visitors{ [&](const Pa::BackspaceStmt *s) { ioLabel(e, s, cstr); }, [&](const Pa::CallStmt *s) { altRet(e, s, cstr); }, [&](const Pa::CloseStmt *s) { ioLabel(e, s, cstr); }, @@ -602,7 +603,7 @@ inline void annotateFuncCFG(AST::FunctionLikeUnit &flu) { annotateEvalListCFG(flu.evals, nullptr); } -const char *evalName(AST::Evaluation &e) { +L::StringRef evalName(AST::Evaluation &e) { return std::visit( Co::visitors{ [](const Pa::AllocateStmt *) { return "AllocateStmt"; }, @@ -703,11 +704,11 @@ const char *evalName(AST::Evaluation &e) { } void dumpEvalList( - llvm::raw_ostream &o, std::list &evals, int indent = 1) { + L::raw_ostream &o, std::list &evals, int indent = 1) { static const std::string white{" ++"}; std::string indentString{white.substr(0, indent * 2)}; for (AST::Evaluation &e : evals) { - const char *name{evalName(e)}; + L::StringRef name{evalName(e)}; if (e.isConstruct()) { o << indentString << "<<" << name << ">>\n"; dumpEvalList(o, *e.getConstructEvals(), indent + 1); @@ -718,8 +719,8 @@ void dumpEvalList( } } -void dumpFunctionLikeUnit(llvm::raw_ostream &o, AST::FunctionLikeUnit &flu) { - const char *unitKind{}; +void dumpFunctionLikeUnit(L::raw_ostream &o, AST::FunctionLikeUnit &flu) { + L::StringRef unitKind{}; std::string name{}; std::string header{}; std::visit(Co::visitors{ @@ -822,7 +823,7 @@ AST::Program *Br::createAST(const Pa::Program &root) { void Br::annotateControl(AST::Program &ast) { for (auto &unit : ast.getUnits()) { - std::visit(common::visitors{ + std::visit(Co::visitors{ [](AST::BlockDataUnit &) {}, [](AST::FunctionLikeUnit &f) { annotateFuncCFG(f); @@ -841,7 +842,7 @@ void Br::annotateControl(AST::Program &ast) { } /// Dump an AST. -void Br::dumpAST(llvm::raw_ostream &o, AST::Program &ast) { +void Br::dumpAST(L::raw_ostream &o, AST::Program &ast) { for (auto &unit : ast.getUnits()) { std::visit( Co::visitors{ diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index ad8cafb567b1..7ed1782259b1 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -47,6 +47,9 @@ using namespace Fortran::burnside; namespace { +L::cl::opt ClDumpPreFir("fdebug-dump-pre-fir", L::cl::init(false), + L::cl::desc("dump the IR tree prior to FIR")); + L::cl::opt ClDisableToDoAssert("disable-burnside-todo", L::cl::desc("disable burnside bridge asserts"), L::cl::init(false), L::cl::Hidden); @@ -72,7 +75,7 @@ constexpr static bool isStopStmt(const Pa::StopStmt &stm) { #define TODO() \ { \ if (ClDisableToDoAssert) \ - mlir::emitError(toLocation(), __FILE__) \ + M::emitError(toLocation(), __FILE__) \ << ":" << __LINE__ << " not implemented"; \ else \ assert(false && "not yet implemented"); \ @@ -621,39 +624,39 @@ class FirConverter : public AbstractConverter { void genFIR(const Pa::OpenMPConstruct &) { TODO(); } void genFIR(const Pa::OmpEndLoopDirective &) { TODO(); } - void genFIR(const parser::AssociateStmt &) { TODO(); } - void genFIR(const parser::EndAssociateStmt &) { TODO(); } - void genFIR(const parser::BlockStmt &) { TODO(); } - void genFIR(const parser::EndBlockStmt &) { TODO(); } - void genFIR(const parser::SelectCaseStmt &) { TODO(); } - void genFIR(const parser::CaseStmt &) { TODO(); } - void genFIR(const parser::EndSelectStmt &) { TODO(); } - void genFIR(const parser::ChangeTeamStmt &) { TODO(); } - void genFIR(const parser::EndChangeTeamStmt &) { TODO(); } - void genFIR(const parser::CriticalStmt &) { TODO(); } - void genFIR(const parser::EndCriticalStmt &) { TODO(); } + void genFIR(const Pa::AssociateStmt &) { TODO(); } + void genFIR(const Pa::EndAssociateStmt &) { TODO(); } + void genFIR(const Pa::BlockStmt &) { TODO(); } + void genFIR(const Pa::EndBlockStmt &) { TODO(); } + void genFIR(const Pa::SelectCaseStmt &) { TODO(); } + void genFIR(const Pa::CaseStmt &) { TODO(); } + void genFIR(const Pa::EndSelectStmt &) { TODO(); } + void genFIR(const Pa::ChangeTeamStmt &) { TODO(); } + void genFIR(const Pa::EndChangeTeamStmt &) { TODO(); } + void genFIR(const Pa::CriticalStmt &) { TODO(); } + void genFIR(const Pa::EndCriticalStmt &) { TODO(); } // Do loop is handled by EvalIterative(), EvalStructuredOp() - void genFIR(const parser::NonLabelDoStmt &) {} // do nothing - void genFIR(const parser::EndDoStmt &) {} // do nothing + void genFIR(const Pa::NonLabelDoStmt &) {} // do nothing + void genFIR(const Pa::EndDoStmt &) {} // do nothing // If-Then-Else is handled by EvalCondGoto(), EvalStructuredOp() - void genFIR(const parser::IfThenStmt &) {} // do nothing - void genFIR(const parser::ElseIfStmt &) {} // do nothing - void genFIR(const parser::ElseStmt &) {} // do nothing - void genFIR(const parser::EndIfStmt &) {} // do nothing - - void genFIR(const parser::SelectRankStmt &) { TODO(); } - void genFIR(const parser::SelectRankCaseStmt &) { TODO(); } - void genFIR(const parser::SelectTypeStmt &) { TODO(); } - void genFIR(const parser::TypeGuardStmt &) { TODO(); } - - void genFIR(const parser::WhereConstructStmt &) { TODO(); } - void genFIR(const parser::MaskedElsewhereStmt &) { TODO(); } - void genFIR(const parser::ElsewhereStmt &) { TODO(); } - void genFIR(const parser::EndWhereStmt &) { TODO(); } - void genFIR(const parser::ForallConstructStmt &) { TODO(); } - void genFIR(const parser::EndForallStmt &) { TODO(); } + void genFIR(const Pa::IfThenStmt &) {} // do nothing + void genFIR(const Pa::ElseIfStmt &) {} // do nothing + void genFIR(const Pa::ElseStmt &) {} // do nothing + void genFIR(const Pa::EndIfStmt &) {} // do nothing + + void genFIR(const Pa::SelectRankStmt &) { TODO(); } + void genFIR(const Pa::SelectRankCaseStmt &) { TODO(); } + void genFIR(const Pa::SelectTypeStmt &) { TODO(); } + void genFIR(const Pa::TypeGuardStmt &) { TODO(); } + + void genFIR(const Pa::WhereConstructStmt &) { TODO(); } + void genFIR(const Pa::MaskedElsewhereStmt &) { TODO(); } + void genFIR(const Pa::ElsewhereStmt &) { TODO(); } + void genFIR(const Pa::EndWhereStmt &) { TODO(); } + void genFIR(const Pa::ForallConstructStmt &) { TODO(); } + void genFIR(const Pa::EndForallStmt &) { TODO(); } // // Statements that do not have control-flow semantics @@ -1090,7 +1093,7 @@ class FirConverter : public AbstractConverter { void run(AST::Program &ast) { // build pruned control for (auto &u : ast.getUnits()) { - std::visit(common::visitors{ + std::visit(Co::visitors{ [&](AST::FunctionLikeUnit &f) { pruneFunc(f); }, [&](AST::ModuleLikeUnit &m) { pruneMod(m); }, [](AST::BlockDataUnit &) { /* do nothing */ }, @@ -1100,7 +1103,7 @@ class FirConverter : public AbstractConverter { // do translation for (auto &u : ast.getUnits()) { - std::visit(common::visitors{ + std::visit(Co::visitors{ [&](AST::FunctionLikeUnit &f) { lowerFunc(f, {}); }, [&](AST::ModuleLikeUnit &m) { lowerMod(m); }, [&](AST::BlockDataUnit &) { TODO(); }, @@ -1134,10 +1137,10 @@ class FirConverter : public AbstractConverter { M::Type genType(SymbolRef sym) override final { return translateSymbolToFIRType(&mlirContext, defaults, sym); } - M::Type genType(common::TypeCategory tc, int kind) override final { + M::Type genType(Co::TypeCategory tc, int kind) override final { return getFIRType(&mlirContext, defaults, tc, kind); } - M::Type genType(common::TypeCategory tc) override final { + M::Type genType(Co::TypeCategory tc) override final { return getFIRType(&mlirContext, defaults, tc); } @@ -1172,8 +1175,8 @@ void Br::BurnsideBridge::lower( const Pa::Program &prg, fir::NameUniquer &uniquer) { AST::Program *ast{Br::createAST(prg)}; Br::annotateControl(*ast); - if (getDumpPreFIR()) { - Br::dumpAST(llvm::errs(), *ast); + if (ClDumpPreFir) { + Br::dumpAST(L::errs(), *ast); } FirConverter converter{*this, uniquer}; converter.run(*ast); diff --git a/lib/burnside/bridge.h b/lib/burnside/bridge.h index a354409f52af..16763f262525 100644 --- a/lib/burnside/bridge.h +++ b/lib/burnside/bridge.h @@ -136,9 +136,6 @@ class BurnsideBridge { /// Cross the bridge from the Fortran parse-tree, etc. to FIR+OpenMP+MLIR void lower(const parser::Program &program, fir::NameUniquer &uniquer); - void dumpPreFIR(bool flag = true) { dumpPreFIR_ = flag; } - bool getDumpPreFIR() { return dumpPreFIR_; } - private: explicit BurnsideBridge(const common::IntrinsicTypeDefaultKinds &defaultKinds, const parser::CookedSource *cooked); @@ -149,7 +146,6 @@ class BurnsideBridge { const parser::CookedSource *cooked; std::unique_ptr context; std::unique_ptr module; - bool dumpPreFIR_{false}; }; } // Fortran::burnside diff --git a/tools/bbc/bbc.cpp b/tools/bbc/bbc.cpp index b1695c7363d9..34e9471c0f35 100644 --- a/tools/bbc/bbc.cpp +++ b/tools/bbc/bbc.cpp @@ -120,7 +120,6 @@ struct DriverOptions { bool debugSemantics{false}; bool measureTree{false}; bool runBackend{true}; - bool dumpPreFIR{false}; bool dumpHLFIR{true}; bool dumpFIR{true}; bool lowerToStd{true}; @@ -273,9 +272,6 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options, auto burnside = Br::BurnsideBridge::create( semanticsContext.defaultKinds(), &parsing.cooked()); fir::KindMapping kindMap{&burnside.getMLIRContext()}; - if (driver.dumpPreFIR) { - burnside.dumpPreFIR(); - } burnside.lower(parseTree, nameUniquer); mlir::ModuleOp mlirModule{burnside.getModule()}; mlir::PassManager pm{mlirModule.getContext()}; @@ -447,8 +443,6 @@ int main(int argc, char *const argv[]) { driver.dumpUnparse = true; } else if (arg == "-funparse-with-symbols") { driver.dumpUnparseWithSymbols = true; - } else if (arg == "-fdebug-dump-pre-fir") { - driver.dumpPreFIR = true; } else if (arg == "-fno-dump-hl-fir") { driver.dumpHLFIR = false; } else if (arg == "-fno-dump-fir") { @@ -515,7 +509,6 @@ int main(int argc, char *const argv[]) { << " -fdebug-resolve-names\n" << " -fdebug-instrumented-parse\n" << " -fdebug-semantics perform semantic checks\n" - << " -fdebug-dump-pre-fir dump the IR tree prior to FIR\n" << " -fdotty print FIR as a dotty graph\n" << " -v -c -o -I -D -U have their usual meanings\n" << " -help print this again\n" From aa90969b67912562cd79490b9d452f0aa50c3216 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Thu, 19 Dec 2019 04:56:34 -0800 Subject: [PATCH 080/123] fix unused var warnings --- lib/burnside/convert-type.cc | 2 +- lib/burnside/intrinsics.cc | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/burnside/convert-type.cc b/lib/burnside/convert-type.cc index 173f82d3d213..b82630dc60f3 100644 --- a/lib/burnside/convert-type.cc +++ b/lib/burnside/convert-type.cc @@ -278,7 +278,7 @@ class TypeBuilder { inputTys.emplace_back(genDummyArgType(*arg)); } } - } else if (auto *proc = symbol->detailsIf()) { + } else if (symbol->detailsIf()) { // TODO Should probably use evaluate::Characteristics for that. TODO(); } else { diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index 253a973c146a..e0d32015a76b 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -647,9 +647,6 @@ static mlir::Value *createExtremumCompare(mlir::Location loc, static constexpr auto integerPredicate{extremum == Extremum::Max ? mlir::CmpIPredicate::sgt : mlir::CmpIPredicate::slt}; - static constexpr auto unorderedCmp{extremum == Extremum::Max - ? fir::CmpFPredicate::UGT - : fir::CmpFPredicate::ULT}; static constexpr auto orderedCmp{extremum == Extremum::Max ? fir::CmpFPredicate::OGT : fir::CmpFPredicate::OLT}; @@ -678,6 +675,9 @@ static mlir::Value *createExtremumCompare(mlir::Location loc, result = builder.create(loc, orderedCmp, left, right); } else if constexpr (behavior == ExtremumBehavior::PgfortranLlvm) { // If one of the operand is a NaN, return left whatever it is. + static constexpr auto unorderedCmp{extremum == Extremum::Max + ? fir::CmpFPredicate::UGT + : fir::CmpFPredicate::ULT}; result = builder.create(loc, unorderedCmp, left, right); } else { // TODO: ieeMinNum/ieeeMaxNum From 06d7370b0e6c7b42deeb219c21702846dbfabd68 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Fri, 20 Dec 2019 02:14:04 -0800 Subject: [PATCH 081/123] Primitive scalar character assignement lowering: This lowering of character assignment works for `c1 = c2` where: - c1 and c2 are scalar variables with an explicit length (constant or not). - c1 and c2 do not overlap. It handles the case where c1 and c2 do not have the same length. The implementation is based on `fir.loop` and does not use any runtime. This is more a proof of concept than a profond design choice. After this commit, it is possible to lower to FIR and to get an functional executable for: SUBROUTINE foo(c1, c2, n) INTEGER :: n CHARACTER(n) :: c1 CHARACTER(10) :: c2 c1 = c2 END SUBROUTINE FIXME/TODO are inserted for: - The case where c1 or c2 is assumed length should "work" once rebased with more up-do-date version of lib/evaluate, though `evaluate::DescriptorInquiry` will also need to be lowered then. - The case where c2 is not a variable needs more work. - To support overlaps, a temporary might be considered. --- lib/burnside/bridge.cc | 97 +++++++++++++++++++++++++++++++++++- lib/burnside/convert-expr.cc | 16 +++++- 2 files changed, 109 insertions(+), 4 deletions(-) diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index 7ed1782259b1..b84b7752d44c 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -678,10 +678,103 @@ class FirConverter : public AbstractConverter { void genFIR(const Pa::WriteStmt &stmt) { genWriteStatement(*this, stmt); } void genFIR(const Pa::AllocateStmt &) { TODO(); } + + void genCharacterAssignement( + const Ev::Assignment::IntrinsicAssignment &assignment) { + // Helper to get address and length from an Expr that is a character + // variable designator + auto getAddrAndLength{[&](const SomeExpr &charDesignatorExpr) + -> std::pair { + auto *addr{genExprAddr(charDesignatorExpr)}; + const auto &charExpr{ + std::get>(charDesignatorExpr.u)}; + // FIXME LEN() should also succeed on character(*) and return a + // DescriptorInquiry, this does not work but should be fixed after FIR f18 + // is rebased with master f18 that contains PR871. + std::optional> lenExpr{charExpr.LEN()}; + assert(lenExpr && "could not get expression to compute character length"); + auto *len{genExprValue(Ev::AsGenericExpr(std::move(*lenExpr)))}; + return {addr, len}; + }}; + + auto lhsAddrAndLen{getAddrAndLength(assignment.lhs)}; + // FIXME: In general an address cannot be obtained for rhs because it does + // not have to be a variable. rhs either needs to be materialized (but this + // just push the problem into assigning into a temp), or each element of the + // character rhs must be computed and directly assigned in the related lhs + // element. + auto rhsAddrAndLen{getAddrAndLength(assignment.rhs)}; + + // Generate the copy. + // FIXME: Currently assumes no overlap (else a temp should be used). + + // Get reference and character type. + assert(lhsAddrAndLen.first && "could not get character variable address"); + auto charRefType{ + lhsAddrAndLen.first->getType().dyn_cast()}; + assert(charRefType && "expected character reference type"); + auto charType{charRefType.getEleTy()}; + // Cast character string to array of character to index it in fir.loop + // This is currently required by fir::CoordinateOp. + fir::SequenceType::Shape shape{-1}; + auto arrayRefCharType{ + fir::ReferenceType::get(fir::SequenceType::get(shape, charType))}; + auto lhsArrayView{builder->create( + toLocation(), arrayRefCharType, lhsAddrAndLen.first)}; + auto rhsArrayView{builder->create( + toLocation(), arrayRefCharType, rhsAddrAndLen.first)}; + + // Build loop to copy rhs + auto indexTy{M::IndexType::get(builder->getContext())}; + auto zero{builder->create( + toLocation(), indexTy, builder->getIntegerAttr(indexTy, 0))}; + auto one{builder->create( + toLocation(), indexTy, builder->getIntegerAttr(indexTy, 1))}; + L::SmallVector step; + step.emplace_back(one); + // Copy the minimum of the lhs and rhs lengths + auto cmpLen{builder->create(toLocation(), M::CmpIPredicate::slt, + lhsAddrAndLen.second, rhsAddrAndLen.second)}; + auto copyLen{builder->create( + toLocation(), cmpLen, lhsAddrAndLen.second, rhsAddrAndLen.second)}; + auto copyMaxIndex{ + builder->create(toLocation(), indexTy, copyLen)}; + auto copyLoop{ + builder->create(toLocation(), zero, copyMaxIndex, step)}; + auto *insPt{builder->getInsertionBlock()}; + builder->setInsertionPointToStart(copyLoop.getBody()); + auto index{copyLoop.getInductionVar()}; + auto lhsElementAddr{builder->create( + toLocation(), charRefType, lhsArrayView, index)}; + auto rhsElementAddr{builder->create( + toLocation(), charRefType, rhsArrayView, index)}; + auto elementVal{builder->create(toLocation(), rhsElementAddr)}; + builder->create(toLocation(), elementVal, lhsElementAddr); + builder->setInsertionPointToEnd(insPt); + + // Build loop to pad lhs with blanks if needed. + auto padMaxIndex{builder->create( + toLocation(), indexTy, lhsAddrAndLen.second)}; + auto byteTy{M::IntegerType::get(8, builder->getContext())}; + auto asciiSpace{builder->create( + toLocation(), byteTy, builder->getIntegerAttr(byteTy, 32))}; + auto blank{ + builder->create(toLocation(), charType, asciiSpace)}; + auto padLoop{builder->create( + toLocation(), copyMaxIndex, padMaxIndex, step)}; + insPt = builder->getInsertionBlock(); + builder->setInsertionPointToStart(padLoop.getBody()); + auto padIndex{padLoop.getInductionVar()}; + auto lhsElementAddrPad{builder->create( + toLocation(), charRefType, lhsArrayView, padIndex)}; + builder->create(toLocation(), blank, lhsElementAddrPad); + builder->setInsertionPointToEnd(insPt); + } + void genFIR(const Pa::AssignmentStmt &stmt) { assert(stmt.typedAssignment && "assignment analysis failed"); // Warning: v->u must become v.u after next f18 rebase - if (auto *assignment{std::get_if( + if (const auto *assignment{std::get_if( &stmt.typedAssignment->v->u)}) { const Se::Symbol *sym{Ev::UnwrapWholeSymbolDataRef(assignment->lhs)}; if (sym && Se::IsAllocatable(*sym)) { @@ -714,7 +807,7 @@ class FirConverter : public AbstractConverter { break; case CharacterCat: // Fortran 2018 10.2.1.3 p10 and p11 - TODO(); + genCharacterAssignement(*assignment); break; case DerivedCat: // Fortran 2018 10.2.1.3 p12 and p13 diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index 93977060607e..b458a286549c 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -228,8 +228,20 @@ class ExprLowering { M::Value *gen(Se::SymbolRef sym) { // FIXME: not all symbols are local - return createTemporary( - getLoc(), builder, symMap, converter.genType(sym), &*sym); + auto *addr{createTemporary( + getLoc(), builder, symMap, converter.genType(sym), &*sym)}; + assert(addr && "failed generating symbol address"); + // Get address from descriptor if symbol has one. + auto type{addr->getType()}; + if (auto boxCharType{type.dyn_cast()}) { + auto refType{fir::ReferenceType::get(boxCharType.getEleTy())}; + auto lenType{mlir::IntegerType::get(64, builder.getContext())}; + addr = builder.create(getLoc(), refType, lenType, addr) + .getResult(0); + } else if (type.isa()) { + TODO(); + } + return addr; } M::Value *gendef(Se::SymbolRef sym) { return gen(sym); } From 1a8de43b55f2ed3f5eec86b582a75f42592498ab Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Fri, 20 Dec 2019 11:22:30 -0800 Subject: [PATCH 082/123] get select_type lowered build changes lower complex and some cleanup --- include/fir/FIROps.h | 6 +- include/fir/FIROps.td | 46 ++++++++++---- include/fir/FIROpsSupport.h | 6 +- include/fir/InternalNames.h | 19 ++++-- lib/burnside/ast-builder.cc | 8 +-- lib/fir/FIROps.cpp | 54 ++++++++++++++--- lib/fir/InternalNames.cpp | 116 +++++++++++++++++++++++++++++++++++- lib/fir/KindMapping.cpp | 3 + lib/fir/StdConverter.cpp | 16 +++-- lib/fir/Tilikum.cpp | 68 ++++++++++++++++----- test/fir/fir-ops.fir | 4 +- tools/tco/CMakeLists.txt | 6 +- tools/tco/tco.cpp | 7 ++- 13 files changed, 298 insertions(+), 61 deletions(-) diff --git a/include/fir/FIROps.h b/include/fir/FIROps.h index d4d656c15d6a..0d21f73ec63b 100644 --- a/include/fir/FIROps.h +++ b/include/fir/FIROps.h @@ -69,10 +69,14 @@ class GlobalOp static llvm::StringRef getOperationName() { return "fir.global"; } static llvm::StringRef getTypeAttrName() { return "type"; } - static void build(mlir::Builder *builder, mlir::OperationState *result, + static void build(mlir::Builder *builder, mlir::OperationState &result, llvm::StringRef name, mlir::Type type, llvm::ArrayRef attrs); + static GlobalOp create(mlir::Location loc, llvm::StringRef name, + mlir::Type type, + llvm::ArrayRef attrs = {}); + /// Operation hooks. static mlir::ParseResult parse(mlir::OpAsmParser &parser, mlir::OperationState &result); diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index 322082ee2aba..a689ef0ffd8a 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -1879,7 +1879,21 @@ def fir_ConvertOp : fir_OneResultOp<"convert", [NoSideEffect]>, }]; } -def fir_GenTypeDescOp : fir_OneResultOp<"gentypedesc", [NoSideEffect]> { +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]>, + Arguments<(ins FortranTypeAttr:$in_type)> { let summary = "generate a type descriptor for a given type"; let description = [{ @@ -1893,30 +1907,36 @@ def fir_GenTypeDescOp : fir_OneResultOp<"gentypedesc", [NoSideEffect]> { if (parser.parseType(intype)) return M::failure(); result.addAttribute("in_type", M::TypeAttr::get(intype)); - M::Type restype; - if (parser.parseColonType(restype) || - parser.addTypeToList(restype, result.types)) + M::Type restype = TypeDescType::get(intype); + if (parser.addTypeToList(restype, result.types)) return M::failure(); return M::success(); }]; - let builders = [OpBuilder< - "Builder *, OperationState &result, mlir::TypeAttr res", - [{ - result.addAttribute("in_type", res); - result.addTypes(res.getValue()); - }] - >]; + let builders = [ + OpBuilder<"Builder *, OperationState &result, mlir::TypeAttr inty"> + ]; let printer = [{ p << getOperationName() << ' ' << getAttr("in_type"); - p << " : " << getType(); p.printOptionalAttrDict(getAttrs(), {"in_type"}); }]; + let verifier = [{ + M::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 M::success(); + }]; + let extraClassDeclaration = [{ mlir::Type getInType() { - return getAttrOfType("in_type").getType(); + // get the type that the type descriptor describes + return getAttrOfType("in_type").getValue(); } }]; } diff --git a/include/fir/FIROpsSupport.h b/include/fir/FIROpsSupport.h index 6a133045975d..450fd20f596e 100644 --- a/include/fir/FIROpsSupport.h +++ b/include/fir/FIROpsSupport.h @@ -66,11 +66,13 @@ inline bool pureCall(mlir::Operation *op) { /// 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::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::StringRef name, mlir::Type type, + llvm::ArrayRef attrs = {}); } // namespace fir diff --git a/include/fir/InternalNames.h b/include/fir/InternalNames.h index 78c58af996b1..2fb47a1d5c07 100644 --- a/include/fir/InternalNames.h +++ b/include/fir/InternalNames.h @@ -15,14 +15,12 @@ #ifndef FIR_INTERNAL_NAMES_H #define FIR_INTERNAL_NAMES_H +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSet.h" #include namespace llvm { -template -class ArrayRef; -template -class Optional; class Twine; } // namespace llvm @@ -41,6 +39,7 @@ namespace fir { struct NameUniquer { enum class IntrinsicType { CHARACTER, COMPLEX, INTEGER, LOGICAL, REAL }; + /// The sort of the unique name enum class NameKind { NOT_UNIQUED, COMMON, @@ -54,7 +53,15 @@ struct NameUniquer { VARIABLE }; + /// Components of an unparsed unique name struct DeconstructedName { + DeconstructedName(llvm::StringRef name) : name{name} {} + DeconstructedName(llvm::ArrayRef modules, + llvm::Optional host, llvm::StringRef name, + llvm::ArrayRef kinds) + : modules{modules.begin(), modules.end()}, host{host}, name{name}, + kinds{kinds.begin(), kinds.end()} {} + llvm::SmallVector modules; llvm::Optional host; std::string name; @@ -99,6 +106,10 @@ struct NameUniquer { llvm::Optional host, llvm::StringRef name, llvm::ArrayRef kinds); + std::string doTypeDescriptor(llvm::ArrayRef modules, + llvm::Optional host, + llvm::StringRef name, + llvm::ArrayRef kinds); /// Unique a (global) variable name std::string doVariable(llvm::ArrayRef modules, diff --git a/lib/burnside/ast-builder.cc b/lib/burnside/ast-builder.cc index 21a4e236c0bb..6627d9272538 100644 --- a/lib/burnside/ast-builder.cc +++ b/lib/burnside/ast-builder.cc @@ -609,7 +609,7 @@ L::StringRef evalName(AST::Evaluation &e) { [](const Pa::AllocateStmt *) { return "AllocateStmt"; }, [](const Pa::ArithmeticIfStmt *) { return "ArithmeticIfStmt"; }, [](const Pa::AssignedGotoStmt *) { return "AssignedGotoStmt"; }, - [](const Pa::AssignmentStmt *x) { return "AssignmentStmt"; }, + [](const Pa::AssignmentStmt *) { return "AssignmentStmt"; }, [](const Pa::AssignStmt *) { return "AssignStmt"; }, [](const Pa::BackspaceStmt *) { return "BackspaceStmt"; }, [](const Pa::CallStmt *) { return "CallStmt"; }, @@ -627,7 +627,7 @@ L::StringRef evalName(AST::Evaluation &e) { [](const Pa::ForallStmt *) { return "ForallStmt"; }, [](const Pa::FormTeamStmt *) { return "FormTeamStmt"; }, [](const Pa::GotoStmt *) { return "GotoStmt"; }, - [](const Pa::IfStmt *x) { return "IfStmt"; }, + [](const Pa::IfStmt *) { return "IfStmt"; }, [](const Pa::InquireStmt *) { return "InquireStmt"; }, [](const Pa::LockStmt *) { return "LockStmt"; }, [](const Pa::NullifyStmt *) { return "NullifyStmt"; }, @@ -677,7 +677,7 @@ L::StringRef evalName(AST::Evaluation &e) { [](const Pa::CaseStmt *) { return "CaseStmt"; }, [](const Pa::ChangeTeamStmt *) { return "ChangeTeamStmt"; }, [](const Pa::CriticalStmt *) { return "CriticalStmt"; }, - [](const Pa::ElseIfStmt *x) { return "ElseIfStmt"; }, + [](const Pa::ElseIfStmt *) { return "ElseIfStmt"; }, [](const Pa::ElseStmt *) { return "ElseStmt"; }, [](const Pa::ElsewhereStmt *) { return "ElsewhereStmt"; }, [](const Pa::EndAssociateStmt *) { return "EndAssociateStmt"; }, @@ -690,7 +690,7 @@ L::StringRef evalName(AST::Evaluation &e) { [](const Pa::EndSelectStmt *) { return "EndSelectStmt"; }, [](const Pa::EndWhereStmt *) { return "EndWhereStmt"; }, [](const Pa::ForallConstructStmt *) { return "ForallConstructStmt"; }, - [](const Pa::IfThenStmt *x) { return "IfThenStmt"; }, + [](const Pa::IfThenStmt *) { return "IfThenStmt"; }, [](const Pa::MaskedElsewhereStmt *) { return "MaskedElsewhereStmt"; }, [](const Pa::NonLabelDoStmt *) { return "NonLabelDoStmt"; }, [](const Pa::SelectCaseStmt *) { return "SelectCaseStmt"; }, diff --git a/lib/fir/FIROps.cpp b/lib/fir/FIROps.cpp index aafd99354c8a..45d4555b4e41 100644 --- a/lib/fir/FIROps.cpp +++ b/lib/fir/FIROps.cpp @@ -327,16 +327,39 @@ void DispatchTableOp::appendTableEntry(M::Operation *op) { front().front().push_back(op); } +// GenTypeDescOp + +#if 0 +void GenTypeDescOp::build(Builder *, OperationState &result, M::Type resty, + M::TypeAttr inty) { + result.addAttribute("in_type", inty); + result.addTypes(resty); +} +#endif + +void GenTypeDescOp::build(Builder *, OperationState &result, M::TypeAttr inty) { + result.addAttribute("in_type", inty); + result.addTypes(TypeDescType::get(inty.getValue())); +} + // GlobalOp -void GlobalOp::build(M::Builder *builder, M::OperationState *result, +GlobalOp GlobalOp::create(M::Location location, L::StringRef name, M::Type type, + L::ArrayRef attrs) { + M::OperationState state(location, "global"); + M::Builder builder(location->getContext()); + GlobalOp::build(&builder, state, name, type, attrs); + return cast(Operation::create(state)); +} + +void GlobalOp::build(M::Builder *builder, M::OperationState &result, L::StringRef name, M::Type type, L::ArrayRef attrs) { - result->addAttribute(getTypeAttrName(), M::TypeAttr::get(type)); - result->addAttribute(M::SymbolTable::getSymbolAttrName(), - builder->getStringAttr(name)); + result.addAttribute(getTypeAttrName(), M::TypeAttr::get(type)); + result.addAttribute(M::SymbolTable::getSymbolAttrName(), + builder->getStringAttr(name)); for (const auto &pair : attrs) - result->addAttribute(pair.first, pair.second); + result.addAttribute(pair.first, pair.second); } M::ParseResult GlobalOp::parse(M::OpAsmParser &parser, @@ -418,6 +441,11 @@ M::ParseResult LoadOp::getElementOf(M::Type &ele, M::Type ref) { // 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(M::Builder *builder, M::OperationState &result, M::Value *lb, M::Value *ub, L::ArrayRef step) { if (step.empty()) @@ -591,12 +619,20 @@ bool isReferenceLike(M::Type type) { } M::FuncOp createFuncOp(M::Location loc, M::ModuleOp module, StringRef name, - M::FunctionType type) { + M::FunctionType type, + L::ArrayRef attrs) { if (auto f = module.lookupSymbol(name)) return f; - auto f = M::FuncOp::create(loc, name, type); - module.push_back(f); - return f; + M::OpBuilder modBuilder(module.getBodyRegion()); + return modBuilder.create(loc, name, type, attrs); +} + +GlobalOp createGlobalOp(M::Location loc, M::ModuleOp module, StringRef name, + M::Type type, L::ArrayRef attrs) { + if (auto g = module.lookupSymbol(name)) + return g; + M::OpBuilder modBuilder(module.getBodyRegion()); + return modBuilder.create(loc, name, type, attrs); } // Tablegen operators diff --git a/lib/fir/InternalNames.cpp b/lib/fir/InternalNames.cpp index f6b160cc44e9..2dcd04114222 100644 --- a/lib/fir/InternalNames.cpp +++ b/lib/fir/InternalNames.cpp @@ -44,6 +44,40 @@ std::string doModulesHost(L::ArrayRef mods, return result; } +inline L::SmallVector +convertToStringRef(L::ArrayRef from) { + L::SmallVector to; + for (auto &f : from) + to.push_back(f); + return to; +} + +inline L::Optional +convertToStringRef(const L::Optional &from) { + L::Optional to; + if (from.hasValue()) + to = from.getValue(); + return to; +} + +std::string readName(L::StringRef uniq, std::size_t &i, std::size_t init, + std::size_t end) { + for (i = init; i < end && uniq[i] >= 'a' && uniq[i] <= 'z'; ++i) { + // do nothing + } + return uniq.substr(init, i); +} + +std::int64_t readInt(L::StringRef uniq, std::size_t &i, std::size_t init, + std::size_t end) { + for (i = init; i < end && uniq[i] >= '0' && uniq[i] <= '9'; ++i) { + // do nothing + } + std::int64_t result; + uniq.substr(init, i).getAsInteger(10, result); + return result; +} + } // namespace L::StringRef fir::NameUniquer::toLower(L::StringRef name) { @@ -149,6 +183,14 @@ std::string fir::NameUniquer::doTypeDescriptor( return result.str(); } +std::string fir::NameUniquer::doTypeDescriptor( + L::ArrayRef modules, L::Optional host, + L::StringRef name, L::ArrayRef kinds) { + auto rmodules = convertToStringRef(modules); + auto rhost = convertToStringRef(host); + return doTypeDescriptor(rmodules, rhost, name, kinds); +} + std::string fir::NameUniquer::doVariable(L::ArrayRef modules, L::StringRef name) { L::Twine result = prefix() + doModules(modules) + "E" + toLower(name); @@ -156,7 +198,75 @@ std::string fir::NameUniquer::doVariable(L::ArrayRef modules, } std::pair -fir::NameUniquer::deconstruct(L::StringRef uniquedName) { - assert(false && "not yet implemented"); - return {}; +fir::NameUniquer::deconstruct(L::StringRef uniq) { + if (uniq.startswith("_Q")) { + L::SmallVector modules; + L::Optional host; + std::string name; + L::SmallVector kinds; + NameKind nk = NameKind::NOT_UNIQUED; + for (std::size_t i = 2, end = uniq.size(); i != end;) { + switch (uniq[i]) { + case 'B': + nk = NameKind::COMMON; + name = readName(uniq, i, i + 1, end); + break; + case 'C': + if (uniq[i + 1] == 'T') { + nk = NameKind::TYPE_DESC; + name = readName(uniq, i, i + 2, end); + } else { + nk = NameKind::INTRINSIC_TYPE_DESC; + name = readName(uniq, i, i + 1, end); + } + break; + case 'D': + nk = NameKind::DISPATCH_TABLE; + assert(uniq[i + 1] == 'T'); + name = readName(uniq, i, i + 2, end); + break; + case 'E': + if (uniq[i + 1] == 'C') { + nk = NameKind::CONSTANT; + name = readName(uniq, i, i + 2, end); + } else { + nk = NameKind::VARIABLE; + name = readName(uniq, i, i + 1, end); + } + break; + case 'P': + nk = NameKind::PROCEDURE; + name = readName(uniq, i, i + 1, end); + break; + case 'Q': + nk = NameKind::GENERATED; + name = readName(uniq, i, i + 1, end); + break; + case 'T': + nk = NameKind::DERIVED_TYPE; + name = readName(uniq, i, i + 1, end); + break; + + case 'M': + case 'S': + modules.push_back(readName(uniq, i, i + 1, end)); + break; + case 'F': + host = readName(uniq, i, i + 1, end); + break; + case 'K': + if (uniq[i + 1] == 'N') + kinds.push_back(-readInt(uniq, i, i + 2, end)); + else + kinds.push_back(readInt(uniq, i, i + 1, end)); + break; + + default: + assert(false && "unknown uniquing code"); + break; + } + } + return {nk, DeconstructedName(modules, host, name, kinds)}; + } + return {NameKind::NOT_UNIQUED, DeconstructedName(uniq)}; } diff --git a/lib/fir/KindMapping.cpp b/lib/fir/KindMapping.cpp index f762e75cc0e7..6860667e2ae2 100644 --- a/lib/fir/KindMapping.cpp +++ b/lib/fir/KindMapping.cpp @@ -66,6 +66,7 @@ LLVMTypeID defaultRealKind(KindTy kind) { } } +// lookup the kind-value given the defaults, the mappings, and a KIND key template RT doLookup(std::function def, std::map> const &map, KindTy kind) { @@ -78,11 +79,13 @@ RT doLookup(std::function def, return def(kind); } +// do a lookup for INTERGER, LOGICAL, or CHARACTER template Bitsize getIntegerLikeBitsize(KindTy kind, MAP const &map) { return doLookup(defaultScalingKind, map, kind); } +// do a lookup for REAL or COMPLEX template LLVMTypeID getFloatLikeTypeID(KindTy kind, MAP const &map) { return doLookup(defaultRealKind, map, kind); diff --git a/lib/fir/StdConverter.cpp b/lib/fir/StdConverter.cpp index a0684a131c82..bb7d9cf37a4d 100644 --- a/lib/fir/StdConverter.cpp +++ b/lib/fir/StdConverter.cpp @@ -25,8 +25,8 @@ #include "mlir/Transforms/DialectConversion.h" #include "llvm/ADT/ArrayRef.h" -// This module performs the conversion of FIR operations to MLIR standard and/or -// LLVM-IR dialects. +// This module performs the conversion of some FIR operations. +// Convert some FIR types to standard dialect? namespace L = llvm; namespace M = mlir; @@ -147,11 +147,15 @@ struct SelectTypeOpConversion : public FIROpConversion { OperandTy destOps, M::ModuleOp module, M::ConversionPatternRewriter &rewriter) { M::Type tydesc = fir::TypeDescType::get(ty); - M::Value *t = rewriter.create(loc, M::TypeAttr::get(tydesc)); - std::vector actuals = {selector, t}; + auto tyattr = M::TypeAttr::get(ty); + M::Value *t = rewriter.create(loc, tydesc, tyattr); + M::Type selty = fir::BoxType::get(rewriter.getNoneType()); + M::Value *csel = rewriter.create(loc, selty, selector); + M::Type tty = fir::ReferenceType::get(rewriter.getNoneType()); + M::Value *ct = rewriter.create(loc, tty, t); + std::vector actuals = {csel, ct}; auto fty = rewriter.getI1Type(); - std::vector argTy = {fir::BoxType::get(rewriter.getNoneType()), - tydesc}; + std::vector argTy = {selty, tty}; L::StringRef funName = exactTest ? "FIXME_exact_type_match" : "FIXME_isa_type_test"; createFuncOp(rewriter.getUnknownLoc(), module, funName, diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index e426b108a74b..a6511027e727 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -229,6 +229,13 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { return M::LLVM::LLVMType::getStructTy(llvmDialect, members); } + // complex --> llvm<"{t,t}"> + M::LLVM::LLVMType convertComplexType(M::ComplexType complex) { + auto eleTy = unwrap(convertType(complex.getElementType())); + L::SmallVector tuple{eleTy, eleTy}; + return M::LLVM::LLVMType::getStructTy(llvmDialect, tuple); + } + // fir.tdesc --> llvm<"i8*"> // FIXME: for now use a void*, however pointer identity is not sufficient for // the f18 object v. class distinction @@ -274,6 +281,8 @@ class FIRToLLVMTypeConverter : public M::LLVMTypeConverter { return convertTypeDescType(tdesc.getContext()); if (auto tuple = t.dyn_cast()) return convertTupleType(tuple); + if (auto cmplx = t.dyn_cast()) + return convertComplexType(cmplx); if (auto none = t.dyn_cast()) return M::LLVM::LLVMType::getStructTy(llvmDialect, {}); return LLVMTypeConverter::convertType(t); @@ -366,6 +375,18 @@ class FIROpConversion : public M::ConversionPattern { FIRToLLVMTypeConverter &lowering; }; +/// Create an LLVM dialect global +void createGlobal(M::Location loc, M::ModuleOp mod, L::StringRef name, + M::LLVM::LLVMType type, + M::ConversionPatternRewriter &rewriter) { + if (mod.lookupSymbol(name)) + return; + M::OpBuilder modBuilder(mod.getBodyRegion()); + modBuilder.create(loc, type, /*isConstant=*/true, + M::LLVM::Linkage::Weak, name, + M::Attribute{}); +} + struct AddrOfOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -455,7 +476,7 @@ M::LLVM::LLVMFuncOp getFree(FreeMemOp op, auto module = op.getParentOfType(); if (auto freeFunc = module.lookupSymbol("free")) return freeFunc; - M::OpBuilder moduleBuilder(op.getParentOfType().getBodyRegion()); + M::OpBuilder moduleBuilder(module.getBodyRegion()); auto voidType = M::LLVM::LLVMType::getVoidTy(dialect); return moduleBuilder.create( rewriter.getUnknownLoc(), "free", @@ -1322,6 +1343,7 @@ struct GenDimsOpConversion : public FIROpConversion { } }; +/// lower a type descriptor to a global constant struct GenTypeDescOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -1329,11 +1351,29 @@ struct GenTypeDescOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto gentypedesc = M::cast(op); - auto ty = unwrap(convertType(gentypedesc.getInType())).getPointerTo(); - std::string name = "fixme"; // FIXME: get the uniqued name - rewriter.replaceOpWithNewOp(gentypedesc, ty, name); + auto loc = gentypedesc.getLoc(); + auto inTy = gentypedesc.getInType(); + auto name = consName(rewriter, inTy); + auto gty = unwrap(convertType(inTy)); + auto pty = gty.getPointerTo(); + auto module = gentypedesc.getParentOfType(); + createGlobal(loc, module, name, gty, rewriter); + rewriter.replaceOpWithNewOp(gentypedesc, pty, name); return matchSuccess(); } + + std::string consName(M::ConversionPatternRewriter &rewriter, + M::Type type) const { + if (auto d = type.dyn_cast()) { + auto name = d.getName(); + auto pair = NameUniquer::deconstruct(name); + return lowering.uniquer.doTypeDescriptor( + pair.second.modules, pair.second.host, pair.second.name, + pair.second.kinds); + } + assert(false); + return {}; + } }; struct GlobalEntryOpConversion : public FIROpConversion { @@ -1613,8 +1653,8 @@ struct UnboxCharOpConversion : public FIROpConversion { M::LLVM::LoadOp genLoadWithIndex(M::Location loc, M::Value *tuple, M::LLVM::LLVMType ty, M::ConversionPatternRewriter &rewriter, - M::MLIRContext *ctx, M::LLVM::LLVMType oty, - M::LLVM::ConstantOp c0, int x) { + M::LLVM::LLVMType oty, M::LLVM::ConstantOp c0, + int x) { auto ax = rewriter.getI32IntegerAttr(x); auto cx = rewriter.create(loc, oty, ax); auto xty = ty.getStructElementType(x); @@ -1637,13 +1677,13 @@ struct UnboxOpConversion : public FIROpConversion { auto oty = lowering.offsetType(); auto c0 = rewriter.create( loc, oty, rewriter.getI32IntegerAttr(0)); - auto ptr = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 0); - auto len = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 1); - auto ver = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 2); - auto rank = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 3); - auto type = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 4); - auto attr = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 5); - auto xtra = genLoadWithIndex(loc, tuple, ty, rewriter, ctx, oty, c0, 6); + auto ptr = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 0); + auto len = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 1); + auto ver = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 2); + auto rank = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 3); + auto type = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 4); + auto attr = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 5); + auto xtra = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 6); // FIXME: add dims, etc. std::vector repls = {ptr, len, ver, rank, type, attr, xtra}; unbox.replaceAllUsesWith(repls); @@ -1678,7 +1718,7 @@ struct UndefOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; M::PatternMatchResult - matchAndRewrite(M::Operation *op, OperandTy operands, + matchAndRewrite(M::Operation *op, OperandTy, M::ConversionPatternRewriter &rewriter) const override { auto undef = M::cast(op); rewriter.replaceOpWithNewOp(undef, diff --git a/test/fir/fir-ops.fir b/test/fir/fir-ops.fir index 634e74e1edbe..b0f90399c948 100644 --- a/test/fir/fir-ops.fir +++ b/test/fir/fir-ops.fir @@ -81,7 +81,7 @@ func @instructions() { %27 = fir.call @box4() : () -> !fir.box> %28 = fir.dispatch "method"(%27) : (!fir.box>) -> i32 %29 = fir.convert %28 : (i32) -> i64 - %30 = fir.gentypedesc !fir.type : !fir.tdesc> + %30 = fir.gentypedesc !fir.type fir.call @user_tdesc(%30) : (!fir.tdesc>) -> () %31 = fir.no_reassoc %29 : i64 fir.call @user_i64(%31) : (i64) -> () @@ -184,7 +184,7 @@ func @bar_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 ] + 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) : diff --git a/tools/tco/CMakeLists.txt b/tools/tco/CMakeLists.txt index 115f949ae573..3332c025d303 100644 --- a/tools/tco/CMakeLists.txt +++ b/tools/tco/CMakeLists.txt @@ -16,9 +16,13 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter -Wno-error") set(LIBS FIR MLIRAffineOps + MLIRIR MLIRLLVMIR MLIRLoopOps + MLIRPass MLIRStandardOps + MLIRStandardToLLVM + MLIRTransforms ) add_llvm_library(FirTcoLib tco.cpp) @@ -33,10 +37,8 @@ set(EXE_LIBS MLIREDSC MLIRLinalg MLIRParser - MLIRPass MLIRStandardToLLVM MLIRSupport - MLIRTransforms MLIRVectorOps MLIRVectorToLLVM ) diff --git a/tools/tco/tco.cpp b/tools/tco/tco.cpp index 09ee4d82ca28..88c5f4efad32 100644 --- a/tools/tco/tco.cpp +++ b/tools/tco/tco.cpp @@ -19,6 +19,7 @@ #include "fir/Transforms/StdConverter.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/raw_ostream.h" @@ -92,7 +93,11 @@ int compileFIR() { return 0; } -int main(int argc, char *const argv[]) { +int main(int argc, char **argv) { + llvm::InitLLVM y(argc, argv); + (void)y; + mlir::registerPassManagerCLOptions(); + mlir::PassPipelineCLParser passPipe("", "Compiler passes to run"); cl::ParseCommandLineOptions(argc, argv, "Tilikum Crossing Opt\n"); return compileFIR(); } From c57e767fc3ddfe7e960ad5d54a7288e435214069 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Sat, 21 Dec 2019 16:50:39 -0800 Subject: [PATCH 083/123] changes to fix rebasing --- CMakeLists.txt | 32 +++++++++++++++++++++----------- lib/burnside/bridge.cc | 3 +-- lib/fir/Tilikum.cpp | 1 - 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e8d56a4f45b..1f0f4b76004e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,7 +144,7 @@ if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR ) include_directories("${LLVM_BINARY_DIR}/include" "${LLVM_MAIN_INCLUDE_DIR}") link_directories("${LLVM_LIBRARY_DIR}") - set(MLIR_INCLUDE_DIRS "") + set(MLIR_BINARY_DIR ${LLVM_BINARY_DIR}) set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin ) set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX} ) @@ -219,9 +219,8 @@ Please install Python or specify the PYTHON_EXECUTABLE CMake variable.") set(BACKEND_PACKAGE_STRING "LLVM ${LLVM_PACKAGE_VERSION}") else() set(BACKEND_PACKAGE_STRING "${PACKAGE_STRING}") - set(MLIR_BINARY_DIR ${LLVM_BINARY_DIR}/tools/mlir) - set(MLIR_SOURCE_DIR ${LLVM_SOURCE_DIR}/../mlir) - set(MLIR_INCLUDE_DIRS "${MLIR_BINARY_DIR}/include ${MLIR_SOURCE_DIR}/include") + set(MLIR_BINARY_DIR ${LLVM_BINARY_DIR}/projects/mlir) + set(MLIR_SOURCE_DIR ${LLVM_SOURCE_DIR}/projects/mlir) endif() # Make sure that our source directory is on the current cmake module path so that @@ -250,14 +249,25 @@ endif() set(FLANG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(FLANG_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) -include_directories(BEFORE - ${FLANG_BINARY_DIR}/include - ${FLANG_SOURCE_DIR}/include - ${MLIR_INCLUDE_DIRS} - ) +if(MLIR_SOURCE_DIR) + include_directories(BEFORE + ${FLANG_BINARY_DIR}/include + ${FLANG_SOURCE_DIR}/include + ${MLIR_BINARY_DIR}/include + ${MLIR_SOURCE_DIR}/include + ) + set(MLIR_MAIN_SRC_DIR ${MLIR_SOURCE_DIR}) + set(MLIR_INCLUDE_DIR ${MLIR_SOURCE_DIR}/include) +else() + include_directories(BEFORE + ${FLANG_BINARY_DIR}/include + ${FLANG_SOURCE_DIR}/include + ${MLIR_BINARY_DIR}/include + ) + set(MLIR_MAIN_SRC_DIR ${MLIR_BINARY_DIR}) + set(MLIR_INCLUDE_DIR ${MLIR_BINARY_DIR}/include) +endif() -set(MLIR_MAIN_SRC_DIR ${MLIR_SOURCE_DIR}) -set(MLIR_INCLUDE_DIR ${MLIR_SOURCE_DIR}/include) set(MLIR_TABLEGEN_EXE mlir-tblgen) set(FLANG_C_INCLUDE_DIRS "" CACHE STRING diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index b84b7752d44c..f2990d8f9ed3 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -773,9 +773,8 @@ class FirConverter : public AbstractConverter { void genFIR(const Pa::AssignmentStmt &stmt) { assert(stmt.typedAssignment && "assignment analysis failed"); - // Warning: v->u must become v.u after next f18 rebase if (const auto *assignment{std::get_if( - &stmt.typedAssignment->v->u)}) { + &stmt.typedAssignment->v.u)}) { const Se::Symbol *sym{Ev::UnwrapWholeSymbolDataRef(assignment->lhs)}; if (sym && Se::IsAllocatable(*sym)) { // Assignment of allocatable are more complex, the lhs diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index a6511027e727..889231491194 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -1670,7 +1670,6 @@ struct UnboxOpConversion : public FIROpConversion { matchAndRewrite(M::Operation *op, OperandTy operands, M::ConversionPatternRewriter &rewriter) const override { auto unbox = M::cast(op); - auto *ctx = unbox.getContext(); auto loc = unbox.getLoc(); auto *tuple = operands[0]; auto ty = unwrap(tuple->getType()); From 66f20af1eb952abbe219a1812bf6a4f4279097d2 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Thu, 2 Jan 2020 13:04:57 -0800 Subject: [PATCH 084/123] port over to MLIR post the merge of MLIR to the monorepo --- CMakeLists.txt | 4 +- README.md | 66 ++++-- include/fir/FIROps.h | 77 +++---- include/fir/FIROps.td | 83 ++++---- lib/burnside/bridge.cc | 83 ++++---- lib/burnside/bridge.h | 20 +- lib/burnside/builder.cc | 24 +-- lib/burnside/builder.h | 26 +-- lib/burnside/complex-handler.h | 76 ++++--- lib/burnside/convert-expr.cc | 262 ++++++++++++------------ lib/burnside/convert-expr.h | 26 +-- lib/burnside/intrinsics.cc | 315 +++++++++++++++-------------- lib/burnside/intrinsics.h | 26 +-- lib/burnside/io.cc | 30 ++- lib/burnside/io.h | 18 +- lib/burnside/runtime.h | 30 ++- lib/fir/FIRDialect.cpp | 20 +- lib/fir/FIROps.cpp | 32 ++- lib/fir/StdConverter.cpp | 30 ++- lib/fir/Tilikum.cpp | 126 ++++++------ lib/fir/Transforms/CSE.cpp | 36 ++-- lib/fir/Transforms/MemToReg.cpp | 44 ++-- lib/fir/Transforms/RewriteLoop.cpp | 22 +- 23 files changed, 695 insertions(+), 781 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f0f4b76004e..a4be83d1b276 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -219,8 +219,8 @@ Please install Python or specify the PYTHON_EXECUTABLE CMake variable.") set(BACKEND_PACKAGE_STRING "LLVM ${LLVM_PACKAGE_VERSION}") else() set(BACKEND_PACKAGE_STRING "${PACKAGE_STRING}") - set(MLIR_BINARY_DIR ${LLVM_BINARY_DIR}/projects/mlir) - set(MLIR_SOURCE_DIR ${LLVM_SOURCE_DIR}/projects/mlir) + set(MLIR_BINARY_DIR ${LLVM_BINARY_DIR}/tools/mlir) + set(MLIR_SOURCE_DIR ${LLVM_SOURCE_DIR}/../mlir) endif() # Make sure that our source directory is on the current cmake module path so that diff --git a/README.md b/README.md index 86a3b8102432..9dceef41e8cb 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,9 @@ Working branch for FIR development. -## Monorepo +## Monorepo now contains MLIR + +### In-tree build This is quite similar to the old way, but with a few subtle differences. @@ -18,31 +20,28 @@ This is quite similar to the old way, but with a few subtle differences. ``` git clone git@github.com:flang-compiler/f18-llvm-project.git - git clone git@github.com:flang-compiler/f18-mlir.git git clone git@github.com:schweitzpgi/f18.git ``` 2. Get "on" the right branches. ``` - (cd f18-llvm-project ; git checkout f18) - (cd f18-mlir ; git checkout f18) - (cd f18 ; git checkout f18) + (cd f18-llvm-project ; git checkout mono) + (cd f18 ; git checkout mono) ``` 3. Setup the LLVM space for in-tree builds. ``` - (cd f18-llvm-project/llvm/projects ; ln -s ../../../f18-mlir mlir) (cd f18-llvm-project ; ln -s ../f18 flang) ``` -4. Create a build space for cmake and make/ninja +4. Create a build space for cmake and make (or ninja) ``` mkdir build cd build - cmake ../f18-llvm-project/llvm -DCMAKE_BUILD_TYPE=Debug -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_ENABLE_PROJECTS=flang -DCMAKE_CXX_STANDARD=17 + cmake ../f18-llvm-project/llvm -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_ENABLE_PROJECTS="flang;mlir" -DCMAKE_CXX_STANDARD=17 -DLLVM_BUILD_TOOLS=On -DLLVM_INSTALL_UTILS=On ``` 5. Build everything @@ -55,10 +54,53 @@ One can, for example, do this with make as follows. Or, of course, use their favorite build tool (such as ninja). +### Out-of-tree build + +1. Get the stuff is the same as above. Get the code from the same repos. + +2. Get on the right branches. Again, same as above. + +3. SKIP step 3 above. We're not going to build `flang` yet. + +4. Create a build space for cmake and make (or ninja) + +``` + mkdir build + cd build + export CC= + export CXX= + cmake -GNinja ../f18-llvm-project/llvm -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_ENABLE_PROJECTS=mlir -DCMAKE_CXX_STANDARD=17 -DLLVM_BUILD_TOOLS=On -DCMAKE_INSTALL_PREFIX= +``` + +5. Build and install + +``` + ninja + ninja install +``` + +6. Add the new installation to your PATH + +``` + PATH=/bin:$PATH +``` + +7. Create a build space for another round of cmake and make (or ninja) + +``` + mkdir build-flang + cd build-flang + cmake -GNinja ../f18 -DLLVM_DIR= -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLLVM_TARGETS_TO_BUILD=X86 -DCMAKE_CXX_STANDARD=17 -DLLVM_BUILD_TOOLS=On -DCMAKE_INSTALL_PREFIX= +``` + +8. Build and install + +``` + ninja + ninja install +``` + ### Problems Despite best efforts, there may be situations where the above repos will -get out of sync, and the build will fail. One may want to try using the -alternate MLIR fork at `git@github.com:schweitzpgi/mlir.git` instead of the -one at `git@github.com:flang-compiler/f18-mlir.git` (see step 1) and repeat -the above process. +get out of sync, and the build will fail. diff --git a/include/fir/FIROps.h b/include/fir/FIROps.h index 0d21f73ec63b..2bf565c1da01 100644 --- a/include/fir/FIROps.h +++ b/include/fir/FIROps.h @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- FIROps.h - FIR operations -------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #ifndef FIR_FIROPS_H #define FIR_FIROPS_H @@ -58,10 +52,9 @@ enum class CmpFPredicate { /// `fir.global` is a typed symbol with an optional list of initializers. class GlobalOp - : public mlir::Op< - GlobalOp, mlir::OpTrait::ZeroOperands, mlir::OpTrait::ZeroResult, - mlir::OpTrait::IsIsolatedFromAbove, - mlir::OpTrait::SingleBlockImplicitTerminator::Impl> { + : public mlir::Op::Impl> { public: using Op::Op; using Op::print; @@ -69,19 +62,17 @@ class GlobalOp static llvm::StringRef getOperationName() { return "fir.global"; } static llvm::StringRef getTypeAttrName() { return "type"; } - static void build(mlir::Builder *builder, mlir::OperationState &result, + static void build(mlir::Builder *builder, OperationState &result, llvm::StringRef name, mlir::Type type, - llvm::ArrayRef attrs); + llvm::ArrayRef attrs); - static GlobalOp create(mlir::Location loc, llvm::StringRef name, - mlir::Type type, - llvm::ArrayRef attrs = {}); + static GlobalOp create(Location loc, llvm::StringRef name, mlir::Type type, + llvm::ArrayRef attrs = {}); /// Operation hooks. - static mlir::ParseResult parse(mlir::OpAsmParser &parser, - mlir::OperationState &result); - void print(mlir::OpAsmPrinter &p); - mlir::LogicalResult verify(); + static ParseResult parse(OpAsmParser &parser, OperationState &result); + void print(OpAsmPrinter &p); + LogicalResult verify(); mlir::Type getType() { return getAttrOfType(getTypeAttrName()).getValue(); @@ -96,24 +87,22 @@ class GlobalOp /// `fir.dispatch_table` is an untyped symbol that is a list of associations /// between method identifiers and a FuncOp symbol. class DispatchTableOp - : public mlir::Op< - DispatchTableOp, mlir::OpTrait::ZeroOperands, - mlir::OpTrait::ZeroResult, mlir::OpTrait::IsIsolatedFromAbove, - mlir::OpTrait::SingleBlockImplicitTerminator::Impl> { + : public mlir::Op::Impl> { public: using Op::Op; static llvm::StringRef getOperationName() { return "fir.dispatch_table"; } - static void build(mlir::Builder *builder, mlir::OperationState *result, + static void build(mlir::Builder *builder, OperationState *result, llvm::StringRef name, mlir::Type type, - llvm::ArrayRef attrs); + llvm::ArrayRef attrs); /// Operation hooks. - static mlir::ParseResult parse(mlir::OpAsmParser &parser, - mlir::OperationState &result); - void print(mlir::OpAsmPrinter &p); - mlir::LogicalResult verify(); + static ParseResult parse(OpAsmParser &parser, OperationState &result); + void print(OpAsmPrinter &p); + LogicalResult verify(); void appendTableEntry(mlir::Operation *op); @@ -121,27 +110,23 @@ class DispatchTableOp mlir::Region &front(); }; -mlir::ParseResult isValidCaseAttr(mlir::Attribute attr); +ParseResult isValidCaseAttr(mlir::Attribute attr); unsigned getCaseArgumentOffset(llvm::ArrayRef cases, unsigned dest); -mlir::ParseResult parseSelector(mlir::OpAsmParser *parser, - mlir::OperationState *result, - mlir::OpAsmParser::OperandType &selector, - mlir::Type &type); +ParseResult parseSelector(OpAsmParser *parser, OperationState *result, + OpAsmParser::OperandType &selector, mlir::Type &type); void buildCmpFOp(Builder *builder, OperationState &result, - CmpFPredicate predicate, Value *lhs, Value *rhs); + CmpFPredicate predicate, Value lhs, Value rhs); void buildCmpCOp(Builder *builder, OperationState &result, - CmpFPredicate predicate, Value *lhs, Value *rhs); -mlir::ParseResult parseCmpfOp(mlir::OpAsmParser &parser, - mlir::OperationState &result); -mlir::ParseResult parseCmpcOp(mlir::OpAsmParser &parser, - mlir::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 "fir/FIROps.h.inc" -LoopOp getForInductionVarOwner(mlir::Value *val); +LoopOp getForInductionVarOwner(mlir::Value val); bool isReferenceLike(mlir::Type type); diff --git a/include/fir/FIROps.td b/include/fir/FIROps.td index a689ef0ffd8a..bf4926dc0f72 100644 --- a/include/fir/FIROps.td +++ b/include/fir/FIROps.td @@ -1,20 +1,15 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- FIROps.td - FIR operation definitions --------------*- tablegen -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// -// Definition of the FIR dialect operations +// 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 +/// +//===----------------------------------------------------------------------===// #ifdef FIR_DIALECT_FIR_OPS #else @@ -126,7 +121,7 @@ class fir_SimpleOp traits> def fir_AllocateOpBuilder : OpBuilder< "Builder *builder, OperationState &result, Type inType," - "ArrayRef sizes = {}, ArrayRef attributes = {}", + "ArrayRef sizes = {}, ArrayRef attributes = {}", [{ result.addTypes(getRefTy(inType)); result.addAttribute("in_type", M::TypeAttr::get(inType)); @@ -137,7 +132,7 @@ def fir_AllocateOpBuilder : OpBuilder< def fir_NamedAllocateOpBuilder : OpBuilder< "Builder *builder, OperationState &result, Type inType, StringRef name," - "ArrayRef sizes = {}, ArrayRef attributes = {}", + "ArrayRef sizes = {}, ArrayRef attributes = {}", [{ result.addTypes(getRefTy(inType)); result.addAttribute("in_type", M::TypeAttr::get(inType)); @@ -149,7 +144,7 @@ def fir_NamedAllocateOpBuilder : OpBuilder< def fir_OneResultOpBuilder : OpBuilder< "Builder *, OperationState &result, Type resultType," - "ArrayRef operands, ArrayRef attributes = {}", + "ArrayRef operands, ArrayRef attributes = {}", [{ if (resultType) result.addTypes(resultType); @@ -294,7 +289,7 @@ def fir_LoadOp : fir_OneResultOp<"load", []>, }]; let builders = [OpBuilder< - "Builder *builder, OperationState &result, Value *refVal", + "Builder *builder, OperationState &result, Value refVal", [{ if (!refVal) { mlir::emitError(result.location, "LoadOp has null argument"); @@ -464,9 +459,9 @@ class fir_SwitchTerminatorOp traits = []> : fir_Op, Arguments<(ins Variadic:$args)>, Results<(outs)> { let builders = [OpBuilder< - "Builder *, OperationState &result, Value *selector," - "ArrayRef properOperands, ArrayRef destinations," - "ArrayRef> operands = {}," + "Builder *, OperationState &result, Value selector," + "ArrayRef properOperands, ArrayRef destinations," + "ArrayRef> operands = {}," "ArrayRef attributes = {}", [{ result.addOperands(selector); @@ -481,14 +476,14 @@ class fir_SwitchTerminatorOp traits = []> : >]; string extraSwitchClassDeclaration = [{ - using Conditions = mlir::Value *; + using Conditions = mlir::Value; constexpr static 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); } + mlir::Value getSelector() { return getOperand(0); } // The number of blocks that may be branched to unsigned getNumDest() { return getOperation()->getNumSuccessors(); } @@ -505,11 +500,11 @@ class fir_IntegralSwitchTerminatorOp ivalues; llvm::SmallVector dests; - llvm::SmallVector, 4> destArgs; + llvm::SmallVector, 4> destArgs; while (true) { M::Attribute ivalue; // Integer or Unit M::Block *dest; - llvm::SmallVector destArg; + llvm::SmallVector destArg; llvm::SmallVector temp; if (parser.parseAttribute(ivalue, "i", temp) || parser.parseComma() || @@ -616,11 +611,11 @@ def fir_SelectCaseOp : fir_SwitchTerminatorOp<"select_case"> { llvm::SmallVector attrs; llvm::SmallVector opers; llvm::SmallVector dests; - llvm::SmallVector, 4> destArgs; + llvm::SmallVector, 4> destArgs; while (true) { M::Attribute attr; M::Block *dest; - llvm::SmallVector destArg; + llvm::SmallVector destArg; llvm::SmallVector temp; if (parser.parseAttribute(attr, "a", temp) || isValidCaseAttr(attr) || @@ -700,7 +695,7 @@ def fir_SelectCaseOp : fir_SwitchTerminatorOp<"select_case"> { let extraClassDeclaration = extraSwitchClassDeclaration#[{ - mlir::Value* getCaseArg(unsigned dest, unsigned ele) { + mlir::Value getCaseArg(unsigned dest, unsigned ele) { assert(ele < 2); assert(dest < getNumConditions()); auto cases = getAttrOfType(AttrName).getValue(); @@ -730,11 +725,11 @@ def fir_SelectTypeOp : fir_SwitchTerminatorOp<"select_type"> { llvm::SmallVector attrs; llvm::SmallVector dests; - llvm::SmallVector, 4> destArgs; + llvm::SmallVector, 4> destArgs; while (true) { M::Attribute attr; M::Block *dest; - llvm::SmallVector destArg; + llvm::SmallVector destArg; llvm::SmallVector temp; if (parser.parseAttribute(attr, "a", temp) || parser.parseComma() || @@ -980,7 +975,7 @@ def fir_EmboxProcOp : fir_Op<"emboxproc", [NoSideEffect]>, let printer = [{ p << getOperationName() << ' ' << getAttr("funcname"); - auto *h = host(); + auto h = host(); if (h) { p << ", "; p.printOperand(h); @@ -1216,7 +1211,7 @@ def fir_CoordinateOp : fir_Op<"coordinate_of", [NoSideEffect]>, let verifier = [{ // Recovering a LEN type parameter only makes sense from a boxed value - for (auto *co : coor()) + for (auto co : coor()) if (auto *s = co->getDefiningOp()) if (dyn_cast_or_null(s)) { if (getNumOperands() != 2) @@ -1276,7 +1271,7 @@ def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoSideEffect]>, let builders = [OpBuilder< "Builder *builder, OperationState &result, StringRef fieldName," - "Type recTy, ArrayRef operands = {}", + "Type recTy, ArrayRef operands = {}", [{ result.addAttribute(fieldAttrName(), builder->getStringAttr(fieldName)); result.addAttribute(typeAttrName(), TypeAttr::get(recTy)); @@ -1320,7 +1315,7 @@ def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoSideEffect]>, p << '('; p.printOperands(lenparams()); auto sep = ") : "; - for (auto *op : lenparams()) { + for (auto op : lenparams()) { p << sep; if (op) p.printType(op->getType()); @@ -1475,8 +1470,8 @@ def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> { OpBuilder<"mlir::Builder *builder, mlir::OperationState &result," "int64_t lowerBound, int64_t upperBound, int64_t step = 1">, OpBuilder<"mlir::Builder *builder, mlir::OperationState &result," - "mlir::Value *lowerBound, mlir::Value *upperBound," - "llvm::ArrayRef step"> + "mlir::Value lowerBound, mlir::Value upperBound," + "llvm::ArrayRef step"> ]; let parser = [{ return parseLoopOp(parser, result); }]; @@ -1516,17 +1511,17 @@ def fir_LoopOp : fir_Op<"loop", [ImplicitFirTerminator]> { let extraClassDeclaration = [{ mlir::Block *getBody() { return ®ion().front(); } - mlir::Value *getInductionVar() { return getBody()->getArgument(0); } + mlir::Value getInductionVar() { return getBody()->getArgument(0); } mlir::OpBuilder getBodyBuilder() { return mlir::OpBuilder(getBody(), std::prev(getBody()->end())); } - void setLowerBound(mlir::Value *bound) { + void setLowerBound(mlir::Value bound) { getOperation()->setOperand(0, bound); } - void setUpperBound(mlir::Value *bound) { + void setUpperBound(mlir::Value bound) { getOperation()->setOperand(1, bound); } - void setStep(mlir::Value *step) { + void setStep(mlir::Value step) { getOperation()->setOperand(2, step); } static char const* getStepKeyword() { return "step"; } @@ -1544,7 +1539,7 @@ def fir_WhereOp : fir_Op<"where", [ImplicitFirTerminator]> { let skipDefaultBuilders = 1; let builders = [ OpBuilder<"Builder *builder, OperationState &result, " - "Value *cond, bool withOtherRegion"> + "Value cond, bool withOtherRegion"> ]; let parser = [{ return parseWhereOp(parser, result); }]; @@ -1750,7 +1745,7 @@ def fir_CmpfOp : fir_Op<"cmpf", let builders = [OpBuilder< "Builder *builder, OperationState &result, CmpFPredicate predicate," - "Value *lhs, Value *rhs", [{ + "Value lhs, Value rhs", [{ fir::buildCmpFOp(builder, result, predicate, lhs, rhs); }]>]; @@ -1798,7 +1793,7 @@ def fir_CmpcOp : fir_Op<"cmpc", let builders = [OpBuilder< "Builder *builder, OperationState &result, CmpFPredicate predicate," - "Value *lhs, Value *rhs", [{ + "Value lhs, Value rhs", [{ fir::buildCmpCOp(builder, result, predicate, lhs, rhs); }]>]; diff --git a/lib/burnside/bridge.cc b/lib/burnside/bridge.cc index f2990d8f9ed3..b4ca23ce0a89 100644 --- a/lib/burnside/bridge.cc +++ b/lib/burnside/bridge.cc @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/burnside/bridge.cc ----------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #include "bridge.h" #include "ast-builder.h" @@ -93,18 +87,18 @@ class FirConverter : public AbstractConverter { // Helper function members // - M::Value *createFIRAddr(M::Location loc, const Se::SomeExpr *expr) { + M::Value createFIRAddr(M::Location loc, const Se::SomeExpr *expr) { return createSomeAddress(loc, *this, *expr, localSymbols, intrinsics); } - M::Value *createFIRExpr(M::Location loc, const Se::SomeExpr *expr) { + M::Value createFIRExpr(M::Location loc, const Se::SomeExpr *expr) { return createSomeExpression(loc, *this, *expr, localSymbols, intrinsics); } - M::Value *createLogicalExprAsI1(M::Location loc, const Se::SomeExpr *expr) { + M::Value createLogicalExprAsI1(M::Location loc, const Se::SomeExpr *expr) { return createI1LogicalExpression( loc, *this, *expr, localSymbols, intrinsics); } - M::Value *createTemporary(M::Location loc, const Se::Symbol &sym) { + M::Value createTemporary(M::Location loc, const Se::Symbol &sym) { return Br::createTemporary(loc, *builder, localSymbols, genType(sym), &sym); } @@ -262,8 +256,8 @@ class FirConverter : public AbstractConverter { /// Generate the cleanup block before the procedure exits void genFIRFunctionReturn(const Se::Symbol &functionSymbol) { const auto &details{functionSymbol.get()}; - M::Value *resultRef{localSymbols.lookupSymbol(details.result())}; - M::Value *r{builder->create(toLocation(), resultRef)}; + M::Value resultRef{localSymbols.lookupSymbol(details.result())}; + M::Value r{builder->create(toLocation(), resultRef)}; builder->create(toLocation(), r); } template void genFIRProcedureExit(const A *) { @@ -287,18 +281,18 @@ class FirConverter : public AbstractConverter { auto targets{findTargetsOf(eval)}; auto *expr{getEvaluationCondition(eval)}; assert(expr && "condition expression missing"); - auto *cond{createLogicalExprAsI1(toLocation(), expr)}; + auto cond{createLogicalExprAsI1(toLocation(), expr)}; genFIRCondBranch(cond, targets[0], targets[1]); } void genFIRCondBranch( - M::Value *cond, AST::Evaluation *trueDest, AST::Evaluation *falseDest) { + M::Value cond, AST::Evaluation *trueDest, AST::Evaluation *falseDest) { using namespace std::placeholders; localEdgeQ.emplace_back(std::bind( - [](M::OpBuilder *builder, M::Block *block, M::Value *cnd, + [](M::OpBuilder *builder, M::Block *block, M::Value cnd, AST::Evaluation *trueDest, AST::Evaluation *falseDest, M::Location location, const LabelMapType &map) { - L::SmallVector blk; + L::SmallVector blk; builder->setInsertionPointToEnd(block); auto tdp{map.find(trueDest)}; auto fdp{map.find(falseDest)}; @@ -354,13 +348,13 @@ class FirConverter : public AbstractConverter { } template void genWhereCondition(fir::WhereOp &where, const A *stmt) { - auto *cond{createLogicalExprAsI1( + auto cond{createLogicalExprAsI1( toLocation(), Se::GetExpr(std::get(stmt->t)))}; where = builder->create(toLocation(), cond, true); switchInsertionPointToWhere(where); } - M::Value *genFIRLoopIndex(const Pa::ScalarExpr &x) { + M::Value genFIRLoopIndex(const Pa::ScalarExpr &x) { return builder->create(toLocation(), M::IndexType::get(&mlirContext), genExprValue(*Se::GetExpr(x))); } @@ -387,9 +381,9 @@ class FirConverter : public AbstractConverter { Co::visitors{ [&](const Pa::LoopControl::Bounds &x) { // create the fir.loop op - M::Value *lo = genFIRLoopIndex(x.lower); - M::Value *hi = genFIRLoopIndex(x.upper); - L::SmallVector step; + M::Value lo = genFIRLoopIndex(x.lower); + M::Value hi = genFIRLoopIndex(x.upper); + L::SmallVector step; if (x.step.has_value()) { step.emplace_back(genExprValue(*Se::GetExpr(*x.step))); } @@ -410,8 +404,7 @@ class FirConverter : public AbstractConverter { [&](const Pa::LoopControl::Concurrent &x) { // FIXME: can project a multi-dimensional space doLoop = builder->create(toLocation(), - (M::Value *)nullptr, (M::Value *)nullptr, - L::ArrayRef{}); + M::Value{}, M::Value{}, L::ArrayRef{}); builder->setInsertionPointToStart(doLoop.getBody()); }, }, @@ -515,11 +508,11 @@ class FirConverter : public AbstractConverter { // FIXME: mangle name M::FuncOp func{getFunc(funName, funTy)}; (void)func; // FIXME - std::vector actuals; + std::vector actuals; for (auto &aa : std::get>(stmt.v.t)) { auto &kw = std::get>(aa.t); auto &arg = std::get(aa.t); - M::Value *fe{nullptr}; + M::Value fe; std::visit(Co::visitors{ [&](const Co::Indirection &e) { // FIXME: needs to match argument, assumes trivial by-ref @@ -546,14 +539,14 @@ class FirConverter : public AbstractConverter { void genFIR(const Pa::WhereStmt &) { TODO(); } void genFIR(const Pa::ComputedGotoStmt &stmt) { auto *exp{Se::GetExpr(std::get(stmt.t))}; - auto *e1{genExprValue(*exp)}; + auto e1{genExprValue(*exp)}; (void)e1; TODO(); } void genFIR(const Pa::ForallStmt &) { TODO(); } void genFIR(const Pa::ArithmeticIfStmt &stmt) { auto *exp{Se::GetExpr(std::get(stmt.t))}; - auto *e1{genExprValue(*exp)}; + auto e1{genExprValue(*exp)}; (void)e1; TODO(); } @@ -684,8 +677,8 @@ class FirConverter : public AbstractConverter { // Helper to get address and length from an Expr that is a character // variable designator auto getAddrAndLength{[&](const SomeExpr &charDesignatorExpr) - -> std::pair { - auto *addr{genExprAddr(charDesignatorExpr)}; + -> std::pair { + M::Value addr = genExprAddr(charDesignatorExpr); const auto &charExpr{ std::get>(charDesignatorExpr.u)}; // FIXME LEN() should also succeed on character(*) and return a @@ -693,7 +686,7 @@ class FirConverter : public AbstractConverter { // is rebased with master f18 that contains PR871. std::optional> lenExpr{charExpr.LEN()}; assert(lenExpr && "could not get expression to compute character length"); - auto *len{genExprValue(Ev::AsGenericExpr(std::move(*lenExpr)))}; + M::Value len = genExprValue(Ev::AsGenericExpr(std::move(*lenExpr))); return {addr, len}; }}; @@ -730,7 +723,7 @@ class FirConverter : public AbstractConverter { toLocation(), indexTy, builder->getIntegerAttr(indexTy, 0))}; auto one{builder->create( toLocation(), indexTy, builder->getIntegerAttr(indexTy, 1))}; - L::SmallVector step; + L::SmallVector step; step.emplace_back(one); // Copy the minimum of the lhs and rhs lengths auto cmpLen{builder->create(toLocation(), M::CmpIPredicate::slt, @@ -896,7 +889,7 @@ class FirConverter : public AbstractConverter { // call FAIL IMAGE in runtime void genFIR(const Pa::FailImageStmt &stmt) { auto callee{genRuntimeFunction(FIRT_FAIL_IMAGE, 0)}; - L::SmallVector operands; // FAIL IMAGE has no args + L::SmallVector operands; // FAIL IMAGE has no args builder->create(toLocation(), callee, operands); } @@ -906,7 +899,7 @@ class FirConverter : public AbstractConverter { genRuntimeFunction(isStopStmt(stm) ? FIRT_STOP : FIRT_ERROR_STOP, defaults.GetDefaultKind(IntegerCat))}; // 2 args: stop-code-opt, quiet-opt - L::SmallVector operands; + L::SmallVector operands; builder->create(toLocation(), callee, operands); } @@ -1123,7 +1116,7 @@ class FirConverter : public AbstractConverter { // TODO: should these be moved to convert-expr? template - M::Value *genCompare(M::Value *lhs, M::Value *rhs) { + M::Value genCompare(M::Value lhs, M::Value rhs) { auto lty{lhs->getType()}; assert(lty == rhs->getType()); if (lty.isIntOrIndex()) { @@ -1139,16 +1132,16 @@ class FirConverter : public AbstractConverter { M::emitError(toLocation(), "cannot generate operation on this type"); return {}; } - M::Value *genGE(M::Value *lhs, M::Value *rhs) { + M::Value genGE(M::Value lhs, M::Value rhs) { return genCompare(lhs, rhs); } - M::Value *genLE(M::Value *lhs, M::Value *rhs) { + M::Value genLE(M::Value lhs, M::Value rhs) { return genCompare(lhs, rhs); } - M::Value *genEQ(M::Value *lhs, M::Value *rhs) { + M::Value genEQ(M::Value lhs, M::Value rhs) { return genCompare(lhs, rhs); } - M::Value *genAND(M::Value *lhs, M::Value *rhs) { + M::Value genAND(M::Value lhs, M::Value rhs) { return builder->create(lhs->getLoc(), lhs, rhs); } @@ -1211,11 +1204,11 @@ class FirConverter : public AbstractConverter { // // AbstractConverter overrides - M::Value *genExprAddr( + M::Value genExprAddr( const SomeExpr &expr, M::Location *loc = nullptr) override final { return createFIRAddr(loc ? *loc : toLocation(), &expr); } - M::Value *genExprValue( + M::Value genExprValue( const SomeExpr &expr, M::Location *loc = nullptr) override final { return createFIRExpr(loc ? *loc : toLocation(), &expr); } diff --git a/lib/burnside/bridge.h b/lib/burnside/bridge.h index 16763f262525..50f58a119ab6 100644 --- a/lib/burnside/bridge.h +++ b/lib/burnside/bridge.h @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/burnside/bridge.h -----------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #ifndef FORTRAN_BURNSIDE_BRIDGE_H_ #define FORTRAN_BURNSIDE_BRIDGE_H_ @@ -69,10 +63,10 @@ class AbstractConverter { // Expressions /// Generate the address of the location holding the expression - virtual mlir::Value *genExprAddr( + virtual mlir::Value genExprAddr( const SomeExpr &, mlir::Location *loc = nullptr) = 0; /// Generate the computations of the expression to produce a value - virtual mlir::Value *genExprValue( + virtual mlir::Value genExprValue( const SomeExpr &, mlir::Location *loc = nullptr) = 0; // diff --git a/lib/burnside/builder.cc b/lib/burnside/builder.cc index b523ffaf097b..60b1e5f97a96 100644 --- a/lib/burnside/builder.cc +++ b/lib/burnside/builder.cc @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/burnside/builder.cc ---------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #include "builder.h" #include "bridge.h" @@ -44,20 +38,20 @@ M::FuncOp B::getNamedFunction(M::ModuleOp module, llvm::StringRef name) { return module.lookupSymbol(name); } -void B::SymMap::addSymbol(Se::SymbolRef symbol, M::Value *value) { +void B::SymMap::addSymbol(Se::SymbolRef symbol, M::Value value) { symbolMap.try_emplace(&*symbol, value); } -M::Value *B::SymMap::lookupSymbol(Se::SymbolRef symbol) { +M::Value B::SymMap::lookupSymbol(Se::SymbolRef symbol) { auto iter{symbolMap.find(&*symbol)}; return (iter == symbolMap.end()) ? nullptr : iter->second; } -void B::SymMap::pushShadowSymbol(Se::SymbolRef symbol, M::Value *value) { +void B::SymMap::pushShadowSymbol(Se::SymbolRef symbol, M::Value value) { // find any existing mapping for symbol auto iter{symbolMap.find(&*symbol)}; const Se::Symbol *sym{nullptr}; - M::Value *val{nullptr}; + M::Value val; // if mapping exists, save it on the shadow stack if (iter != symbolMap.end()) { sym = iter->first; diff --git a/lib/burnside/builder.h b/lib/burnside/builder.h index 8f663c59f5ee..39ae62b6c4eb 100644 --- a/lib/burnside/builder.h +++ b/lib/burnside/builder.h @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/burnside/builder.h ----------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #ifndef FORTRAN_BURNSIDE_BUILDER_H_ #define FORTRAN_BURNSIDE_BUILDER_H_ @@ -44,15 +38,15 @@ namespace burnside { class AbstractConverter; class SymMap { - llvm::DenseMap symbolMap; - std::vector> shadowStack; + llvm::DenseMap symbolMap; + std::vector> shadowStack; public: - void addSymbol(semantics::SymbolRef symbol, mlir::Value *value); + void addSymbol(semantics::SymbolRef symbol, mlir::Value value); - mlir::Value *lookupSymbol(semantics::SymbolRef symbol); + mlir::Value lookupSymbol(semantics::SymbolRef symbol); - void pushShadowSymbol(semantics::SymbolRef symbol, mlir::Value *value); + void pushShadowSymbol(semantics::SymbolRef symbol, mlir::Value value); void popShadowSymbol() { shadowStack.pop_back(); } void clear() { diff --git a/lib/burnside/complex-handler.h b/lib/burnside/complex-handler.h index 4400e98a2b60..c16c192462d4 100644 --- a/lib/burnside/complex-handler.h +++ b/lib/burnside/complex-handler.h @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/burnside/complex-handler.h --------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #ifndef FORTRAN_BURNSIDE_COMPLEX_HANDLER_H_ #define FORTRAN_BURNSIDE_COMPLEX_HANDLER_H_ @@ -31,14 +25,15 @@ class ComplexHandler { enum class Part { Real = 0, Imag = 1 }; ComplexHandler(mlir::OpBuilder &b, mlir::Location l) : builder{b}, loc{l} {} + mlir::Type getComplexPartType(fir::KindTy complexKind) { return convertReal(builder.getContext(), complexKind); } - mlir::Value *createComplex( - fir::KindTy kind, mlir::Value *real, mlir::Value *imag) { + mlir::Value createComplex( + fir::KindTy kind, mlir::Value real, mlir::Value imag) { mlir::Type complexTy{fir::CplxType::get(builder.getContext(), kind)}; - mlir::Value *und{builder.create(loc, complexTy)}; + mlir::Value und = builder.create(loc, complexTy); return insert(insert(und, real), imag); } @@ -46,59 +41,56 @@ class ComplexHandler { mlir::Type getComplexPartType(mlir::Type complexType) { return getComplexPartType(complexType.cast().getFKind()); } - mlir::Type getComplexPartType(mlir::Value *cplx) { + mlir::Type getComplexPartType(mlir::Value cplx) { assert(cplx != nullptr); return getComplexPartType(cplx->getType()); } - template mlir::Value *extract(mlir::Value *cplx) { + template mlir::Value extract(mlir::Value cplx) { return builder.create( loc, getComplexPartType(cplx), cplx, getPartId()); } - template - mlir::Value *insert(mlir::Value *cplx, mlir::Value *part) { + template mlir::Value insert(mlir::Value cplx, mlir::Value part) { assert(cplx != nullptr); return builder.create( loc, cplx->getType(), cplx, part, getPartId()); } /// Complex part access helper dynamic versions - mlir::Value *extractComplexPart(mlir::Value *cplx, bool isImagPart) { + mlir::Value extractComplexPart(mlir::Value cplx, bool isImagPart) { return isImagPart ? extract(cplx) : extract(cplx); } - mlir::Value *insertComplexPart( - mlir::Value *cplx, mlir::Value *part, bool isImagPart) { + mlir::Value insertComplexPart( + mlir::Value cplx, mlir::Value part, bool isImagPart) { return isImagPart ? insert(cplx, part) : insert(cplx, part); } // Complex operation helpers - mlir::Value *createComplexCompare( - mlir::Value *cplx1, mlir::Value *cplx2, bool eq) { - mlir::Value *real1{extract(cplx1)}; - mlir::Value *real2{extract(cplx2)}; - mlir::Value *imag1{extract(cplx1)}; - mlir::Value *imag2{extract(cplx2)}; - - mlir::CmpFPredicate predicate{ - eq ? mlir::CmpFPredicate::UEQ : mlir::CmpFPredicate::UNE}; - mlir::Value *realCmp{ - builder.create(loc, predicate, real1, real2).getResult()}; - mlir::Value *imagCmp{ - builder.create(loc, predicate, imag1, imag2).getResult()}; - - if (eq) { + mlir::Value createComplexCompare( + mlir::Value cplx1, mlir::Value cplx2, bool eq) { + mlir::Value real1 = extract(cplx1); + mlir::Value real2 = extract(cplx2); + mlir::Value imag1 = extract(cplx1); + mlir::Value imag2 = extract(cplx2); + + mlir::CmpFPredicate predicate = + eq ? mlir::CmpFPredicate::UEQ : mlir::CmpFPredicate::UNE; + mlir::Value realCmp = + builder.create(loc, predicate, real1, real2).getResult(); + mlir::Value imagCmp = + builder.create(loc, predicate, imag1, imag2).getResult(); + + if (eq) return builder.create(loc, realCmp, imagCmp).getResult(); - } else { - return builder.create(loc, realCmp, imagCmp).getResult(); - } + return builder.create(loc, realCmp, imagCmp).getResult(); } private: // Make mlir ConstantOp from template part id. - template inline mlir::Value *getPartId() { - auto type{mlir::IntegerType::get(32, builder.getContext())}; - auto attr{builder.getIntegerAttr(type, static_cast(partId))}; + template inline mlir::Value getPartId() { + auto type = mlir::IntegerType::get(32, builder.getContext()); + auto attr = builder.getIntegerAttr(type, static_cast(partId)); return builder.create(loc, type, attr).getResult(); } diff --git a/lib/burnside/convert-expr.cc b/lib/burnside/convert-expr.cc index b458a286549c..1265cb2cb743 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/burnside/convert-expr.cc @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/burnside/convert-expr.cc ----------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #include "convert-expr.h" #include "bridge.h" @@ -114,7 +108,7 @@ class ExprLowering { /// Generate an integral constant of `value` template - M::Value *genIntegerConstant(M::MLIRContext *context, std::int64_t value) { + M::Value genIntegerConstant(M::MLIRContext *context, std::int64_t value) { M::Type type{converter.genType(IntegerCat, KIND)}; auto attr{builder.getIntegerAttr(type, value)}; auto res{builder.create(getLoc(), type, attr)}; @@ -122,14 +116,14 @@ class ExprLowering { } /// Generate a logical/boolean constant of `value` - M::Value *genLogicalConstantAsI1(M::MLIRContext *context, bool value) { + M::Value genLogicalConstantAsI1(M::MLIRContext *context, bool value) { M::Type i1Type{M::IntegerType::get(1, builder.getContext())}; auto attr{builder.getIntegerAttr(i1Type, value ? 1 : 0)}; return builder.create(getLoc(), i1Type, attr).getResult(); } template - M::Value *genRealConstant(M::MLIRContext *context, L::APFloat const &value) { + M::Value genRealConstant(M::MLIRContext *context, L::APFloat const &value) { M::Type fltTy{convertReal(context, KIND)}; auto attr{builder.getFloatAttr(fltTy, value)}; auto res{builder.create(getLoc(), fltTy, attr)}; @@ -141,16 +135,16 @@ class ExprLowering { } template - M::Value *createBinaryOp(A const &ex, M::Value *lhs, M::Value *rhs) { + M::Value createBinaryOp(A const &ex, M::Value lhs, M::Value rhs) { assert(lhs && rhs && "argument did not lower"); auto x = builder.create(getLoc(), lhs, rhs); return x.getResult(); } template - M::Value *createBinaryOp(A const &ex, M::Value *rhs) { + M::Value createBinaryOp(A const &ex, M::Value rhs) { return createBinaryOp(ex, genval(ex.left()), rhs); } - template M::Value *createBinaryOp(A const &ex) { + template M::Value createBinaryOp(A const &ex) { return createBinaryOp(ex, genval(ex.left()), genval(ex.right())); } @@ -191,8 +185,8 @@ class ExprLowering { /// Create a call to a Fortran runtime entry point template - M::Value *createBinaryFIRTCall(A const &ex, RuntimeEntryCode callee) { - L::SmallVector operands; + M::Value createBinaryFIRTCall(A const &ex, RuntimeEntryCode callee) { + L::SmallVector operands; operands.push_back(genval(ex.left())); operands.push_back(genval(ex.right())); M::FunctionType funTy = createFunctionType(); @@ -202,34 +196,34 @@ class ExprLowering { } template - M::Value *createCompareOp( - A const &ex, M::CmpIPredicate pred, M::Value *lhs, M::Value *rhs) { + M::Value createCompareOp( + A const &ex, M::CmpIPredicate pred, M::Value lhs, M::Value rhs) { assert(lhs && rhs && "argument did not lower"); auto x = builder.create(getLoc(), pred, lhs, rhs); return x.getResult(); } template - M::Value *createCompareOp(A const &ex, M::CmpIPredicate pred) { + M::Value createCompareOp(A const &ex, M::CmpIPredicate pred) { return createCompareOp( ex, pred, genval(ex.left()), genval(ex.right())); } template - M::Value *createFltCmpOp( - A const &ex, fir::CmpFPredicate pred, M::Value *lhs, M::Value *rhs) { + M::Value createFltCmpOp( + A const &ex, fir::CmpFPredicate pred, M::Value lhs, M::Value rhs) { assert(lhs && rhs && "argument did not lower"); auto x = builder.create(getLoc(), pred, lhs, rhs); return x.getResult(); } template - M::Value *createFltCmpOp(A const &ex, fir::CmpFPredicate pred) { + M::Value createFltCmpOp(A const &ex, fir::CmpFPredicate pred) { return createFltCmpOp( ex, pred, genval(ex.left()), genval(ex.right())); } - M::Value *gen(Se::SymbolRef sym) { + M::Value gen(Se::SymbolRef sym) { // FIXME: not all symbols are local - auto *addr{createTemporary( - getLoc(), builder, symMap, converter.genType(sym), &*sym)}; + M::Value addr = createTemporary( + getLoc(), builder, symMap, converter.genType(sym), &*sym); assert(addr && "failed generating symbol address"); // Get address from descriptor if symbol has one. auto type{addr->getType()}; @@ -243,24 +237,22 @@ class ExprLowering { } return addr; } - M::Value *gendef(Se::SymbolRef sym) { return gen(sym); } + M::Value gendef(Se::SymbolRef sym) { return gen(sym); } - M::Value *genval(Se::SymbolRef sym) { + M::Value genval(Se::SymbolRef sym) { #if 0 // Do not load the same symbols several time in one expression. // Fortran guarantees variable value must be the same wherever it // appears in one expression. - if (mlir::Value * loaded{loadedSymbols.lookupSymbol(sym)}) { + if (mlir::Value loaded = loadedSymbols.lookupSymbol(sym)) return loaded; - } else { - mlir::Value *load{builder.create(getLoc(), gen(sym))}; - loadedSymbols.addSymbol(sym, load); - return load; - } + mlir::Value load = builder.create(getLoc(), gen(sym)); + loadedSymbols.addSymbol(sym, load); + return load; #else // Multiple loads should be CSEd. If they aren't, it's a bug in CSE that // should not use a workaround here. - M::Value *var{gen(sym)}; + auto var = gen(sym); if (fir::isReferenceLike(var->getType())) { return builder.create(getLoc(), var); } @@ -268,24 +260,24 @@ class ExprLowering { #endif } - M::Value *genval(Ev::BOZLiteralConstant const &) { TODO(); } - M::Value *genval(Ev::ProcedureRef const &) { TODO(); } - M::Value *genval(Ev::ProcedureDesignator const &) { TODO(); } - M::Value *genval(Ev::NullPointer const &) { TODO(); } - M::Value *genval(Ev::StructureConstructor const &) { TODO(); } - M::Value *genval(Ev::ImpliedDoIndex const &) { TODO(); } - M::Value *genval(Ev::DescriptorInquiry const &) { TODO(); } - template M::Value *genval(Ev::TypeParamInquiry const &) { + M::Value genval(Ev::BOZLiteralConstant const &) { TODO(); } + M::Value genval(Ev::ProcedureRef const &) { TODO(); } + M::Value genval(Ev::ProcedureDesignator const &) { TODO(); } + M::Value genval(Ev::NullPointer const &) { TODO(); } + M::Value genval(Ev::StructureConstructor const &) { TODO(); } + M::Value genval(Ev::ImpliedDoIndex const &) { TODO(); } + M::Value genval(Ev::DescriptorInquiry const &) { TODO(); } + template M::Value genval(Ev::TypeParamInquiry const &) { TODO(); } - template M::Value *genval(Ev::ComplexComponent const &part) { + template M::Value genval(Ev::ComplexComponent const &part) { return ComplexHandler{builder, getLoc()}.extractComplexPart( genval(part.left()), part.isImaginaryPart); } template - M::Value *genval(Ev::Negate> const &op) { + M::Value genval(Ev::Negate> const &op) { auto input{genval(op.left())}; if constexpr (TC == IntegerCat) { // Currently no Standard/FIR op for integer negation. @@ -300,7 +292,7 @@ class ExprLowering { } template - M::Value *genval(Ev::Add> const &op) { + M::Value genval(Ev::Add> const &op) { if constexpr (TC == IntegerCat) { return createBinaryOp(op); } else if constexpr (TC == RealCat) { @@ -311,7 +303,7 @@ class ExprLowering { } } template - M::Value *genval(Ev::Subtract> const &op) { + M::Value genval(Ev::Subtract> const &op) { if constexpr (TC == IntegerCat) { return createBinaryOp(op); } else if constexpr (TC == RealCat) { @@ -322,7 +314,7 @@ class ExprLowering { } } template - M::Value *genval(Ev::Multiply> const &op) { + M::Value genval(Ev::Multiply> const &op) { if constexpr (TC == IntegerCat) { return createBinaryOp(op); } else if constexpr (TC == RealCat) { @@ -333,9 +325,9 @@ class ExprLowering { } } template - M::Value *genval(Ev::Divide> const &op) { + M::Value genval(Ev::Divide> const &op) { if constexpr (TC == IntegerCat) { - return createBinaryOp(op); + return createBinaryOp(op); } else if constexpr (TC == RealCat) { return createBinaryOp(op); } else { @@ -344,34 +336,34 @@ class ExprLowering { } } template - M::Value *genval(Ev::Power> const &op) { - llvm::SmallVector operands{ + M::Value genval(Ev::Power> const &op) { + llvm::SmallVector operands{ genval(op.left()), genval(op.right())}; M::Type ty{converter.genType(TC, KIND)}; return intrinsics.genval(getLoc(), builder, "pow", ty, operands); } template - M::Value *genval(Ev::RealToIntPower> const &op) { + M::Value genval(Ev::RealToIntPower> const &op) { // TODO: runtime as limited integer kind support. Look if the conversions // are ok - llvm::SmallVector operands{ + llvm::SmallVector operands{ genval(op.left()), genval(op.right())}; M::Type ty{converter.genType(TC, KIND)}; return intrinsics.genval(getLoc(), builder, "pow", ty, operands); } - template M::Value *genval(Ev::ComplexConstructor const &op) { + template M::Value genval(Ev::ComplexConstructor const &op) { return ComplexHandler{builder, getLoc()}.createComplex( KIND, genval(op.left()), genval(op.right())); } - template M::Value *genval(Ev::Concat const &op) { + template M::Value genval(Ev::Concat const &op) { // TODO this is a bogus call return createBinaryFIRTCall(op, FIRT_CONCAT); } /// MIN and MAX operations template - M::Value *genval(Ev::Extremum> const &op) { + M::Value genval(Ev::Extremum> const &op) { if constexpr (TC == IntegerCat) { return createBinaryFIRTCall( op, op.ordering == Ev::Ordering::Greater ? FIRT_MAX : FIRT_MIN); @@ -380,11 +372,11 @@ class ExprLowering { } } - template M::Value *genval(Ev::SetLength const &) { TODO(); } + template M::Value genval(Ev::SetLength const &) { TODO(); } template - M::Value *genval(Ev::Relational> const &op) { - mlir::Value *result{nullptr}; + M::Value genval(Ev::Relational> const &op) { + mlir::Value result{nullptr}; if constexpr (TC == IntegerCat) { result = createCompareOp(op, translateRelational(op.opr)); } else if constexpr (TC == RealCat) { @@ -404,14 +396,14 @@ class ExprLowering { return result; } - M::Value *genval(Ev::Relational const &op) { + M::Value genval(Ev::Relational const &op) { return std::visit([&](const auto &x) { return genval(x); }, op.u); } template - M::Value *genval(Ev::Convert, TC2> const &convert) { + M::Value genval(Ev::Convert, TC2> const &convert) { auto ty{converter.genType(TC1, KIND)}; - M::Value *operand{genval(convert.left())}; + M::Value operand{genval(convert.left())}; if (TC1 == LogicalCat && genLogicalAsI1) { // If an i1 result is needed, it does not make sens to convert between // `fir.logical` types to later convert back to the result to i1. @@ -420,9 +412,9 @@ class ExprLowering { return builder.create(getLoc(), ty, operand); } - template M::Value *genval(Ev::Parentheses const &) { TODO(); } + template M::Value genval(Ev::Parentheses const &) { TODO(); } - template M::Value *genval(const Ev::Not &op) { + template M::Value genval(const Ev::Not &op) { // Request operands to be generated as `i1` and restore after this scope. auto restorer{common::ScopedSet(genLogicalAsI1, true)}; auto *context{builder.getContext()}; @@ -431,10 +423,10 @@ class ExprLowering { return builder.create(getLoc(), logical, one).getResult(); } - template M::Value *genval(Ev::LogicalOperation const &op) { + template M::Value genval(Ev::LogicalOperation const &op) { // Request operands to be generated as `i1` and restore after this scope. auto restorer{common::ScopedSet(genLogicalAsI1, true)}; - mlir::Value *result{nullptr}; + mlir::Value result; switch (op.logicalOperator) { case Ev::LogicalOperator::And: result = createBinaryOp(op); break; case Ev::LogicalOperator::Or: result = createBinaryOp(op); break; @@ -456,7 +448,7 @@ class ExprLowering { } template - M::Value *genval(Ev::Constant> const &con) { + M::Value genval(Ev::Constant> const &con) { // TODO: // - character type constant // - array constant not handled @@ -514,7 +506,7 @@ class ExprLowering { } template - M::Value *genval(Ev::Constant> const &con) { + M::Value genval(Ev::Constant> const &con) { if constexpr (TC == IntegerCat) { auto opt = (*con).ToInt64(); M::Type type{getSomeKindInteger()}; @@ -527,18 +519,18 @@ class ExprLowering { } } - template M::Value *genval(Ev::ArrayConstructor const &) { + template M::Value genval(Ev::ArrayConstructor const &) { TODO(); } - M::Value *gen(Ev::ComplexPart const &) { TODO(); } - M::Value *gendef(Ev::ComplexPart const &cp) { return gen(cp); } - M::Value *genval(Ev::ComplexPart const &) { TODO(); } - M::Value *gen(const Ev::Substring &) { TODO(); } - M::Value *gendef(const Ev::Substring &ss) { return gen(ss); } - M::Value *genval(const Ev::Substring &) { TODO(); } - M::Value *genval(const Ev::Triplet &trip) { TODO(); } + M::Value gen(Ev::ComplexPart const &) { TODO(); } + M::Value gendef(Ev::ComplexPart const &cp) { return gen(cp); } + M::Value genval(Ev::ComplexPart const &) { TODO(); } + M::Value gen(const Ev::Substring &) { TODO(); } + M::Value gendef(const Ev::Substring &ss) { return gen(ss); } + M::Value genval(const Ev::Substring &) { TODO(); } + M::Value genval(const Ev::Triplet &trip) { TODO(); } - M::Value *genval(const Ev::Subscript &subs) { + M::Value genval(const Ev::Subscript &subs) { return std::visit(Co::visitors{ [&](const Ev::IndirectSubscriptIntegerExpr &x) { return genval(x.value()); @@ -548,11 +540,11 @@ class ExprLowering { subs.u); } - M::Value *gen(const Ev::DataRef &dref) { + M::Value gen(const Ev::DataRef &dref) { return std::visit([&](const auto &x) { return gen(x); }, dref.u); } - M::Value *gendef(Ev::DataRef const &dref) { return gen(dref); } - M::Value *genval(Ev::DataRef const &dref) { + M::Value gendef(Ev::DataRef const &dref) { return gen(dref); } + M::Value genval(Ev::DataRef const &dref) { return std::visit([&](const auto &x) { return genval(x); }, dref.u); } @@ -570,10 +562,10 @@ class ExprLowering { } // Return the coordinate of the component reference - M::Value *gen(const Ev::Component &cmpt) { + M::Value gen(const Ev::Component &cmpt) { std::list list; auto *base{reverseComponents(cmpt, list)}; - L::SmallVector coorArgs; + L::SmallVector coorArgs; auto obj{gen(*base)}; const Se::Symbol *sym{nullptr}; M::Type ty{converter.genType(*sym)}; @@ -588,8 +580,8 @@ class ExprLowering { ty = fir::ReferenceType::get(ty); return builder.create(getLoc(), ty, obj, coorArgs); } - M::Value *gendef(const Ev::Component &cmpt) { return gen(cmpt); } - M::Value *genval(const Ev::Component &cmpt) { + M::Value gendef(const Ev::Component &cmpt) { return gen(cmpt); } + M::Value genval(const Ev::Component &cmpt) { return builder.create(getLoc(), gen(cmpt)); } @@ -613,13 +605,10 @@ class ExprLowering { } // Return the coordinate of the array reference - M::Value *gen(Ev::ArrayRef const &aref) { - M::Value *base; - if (aref.base().IsSymbol()) - base = gen(aref.base().GetFirstSymbol()); - else - base = gen(aref.base().GetComponent()); - llvm::SmallVector args; + M::Value gen(Ev::ArrayRef const &aref) { + M::Value base = aref.base().IsSymbol() ? gen(aref.base().GetFirstSymbol()) + : gen(aref.base().GetComponent()); + llvm::SmallVector args; for (auto &subsc : aref.subscript()) { args.push_back(genval(subsc)); } @@ -627,48 +616,49 @@ class ExprLowering { ty = fir::ReferenceType::get(ty); return builder.create(getLoc(), ty, base, args); } - M::Value *gendef(const Ev::ArrayRef &aref) { return gen(aref); } - M::Value *genval(const Ev::ArrayRef &aref) { + + M::Value gendef(const Ev::ArrayRef &aref) { return gen(aref); } + M::Value genval(const Ev::ArrayRef &aref) { return builder.create(getLoc(), gen(aref)); } // Return a coordinate of the coarray reference. This is necessary as a // Component may have a CoarrayRef as its base coordinate. - M::Value *gen(Ev::CoarrayRef const &coref) { + M::Value gen(Ev::CoarrayRef const &coref) { // FIXME: need to visit the cosubscripts... // return gen(coref.base()); TODO(); } - M::Value *gendef(const Ev::CoarrayRef &coref) { return gen(coref); } - M::Value *genval(const Ev::CoarrayRef &coref) { + M::Value gendef(const Ev::CoarrayRef &coref) { return gen(coref); } + M::Value genval(const Ev::CoarrayRef &coref) { return builder.create(getLoc(), gen(coref)); } - template M::Value *gen(Ev::Designator const &des) { + template M::Value gen(Ev::Designator const &des) { return std::visit([&](const auto &x) { return gen(x); }, des.u); } - template M::Value *gendef(Ev::Designator const &des) { + template M::Value gendef(Ev::Designator const &des) { return gen(des); } - template M::Value *genval(Ev::Designator const &des) { + template M::Value genval(Ev::Designator const &des) { return std::visit([&](const auto &x) { return genval(x); }, des.u); } // call a function - template M::Value *gen(const Ev::FunctionRef &funRef) { + template M::Value gen(const Ev::FunctionRef &funRef) { TODO(); } - template M::Value *gendef(const Ev::FunctionRef &funRef) { + template M::Value gendef(const Ev::FunctionRef &funRef) { return gen(funRef); } - template M::Value *genval(const Ev::FunctionRef &funRef) { + template M::Value genval(const Ev::FunctionRef &funRef) { TODO(); // Derived type functions (user + intrinsics) } template - M::Value *genval(const Ev::FunctionRef> &funRef) { + M::Value genval(const Ev::FunctionRef> &funRef) { if (const auto &intrinsic{funRef.proc().GetSpecificIntrinsic()}) { M::Type ty{converter.genType(TC, KIND)}; - L::SmallVector operands; + L::SmallVector operands; // Lower arguments // For now, logical arguments for intrinsic are lowered to `fir.logical` // so that TRANSFER can work. For some arguments, it could lead to useless @@ -690,7 +680,7 @@ class ExprLowering { // implicit interface implementation only // TODO: explicit interface L::SmallVector argTypes; - L::SmallVector operands; + L::SmallVector operands; // Logical arguments of user functions must be lowered to `fir.logical` // and not `i1`. auto restorer{common::ScopedSet(genLogicalAsI1, false)}; @@ -700,7 +690,7 @@ class ExprLowering { const auto *expr{arg->UnwrapExpr()}; assert(expr && "assumed type argument requires explicit interface"); if (const Se::Symbol * sym{Ev::UnwrapWholeSymbolDataRef(*expr)}) { - M::Value *argRef{symMap.lookupSymbol(*sym)}; + M::Value argRef{symMap.lookupSymbol(*sym)}; assert(argRef && "could not get symbol reference"); argTypes.push_back(argRef->getType()); operands.push_back(argRef); @@ -722,20 +712,20 @@ class ExprLowering { } } - template M::Value *gen(const Ev::Expr &exp) { + template M::Value gen(const Ev::Expr &exp) { // must be a designator or function-reference (R902) return std::visit([&](const auto &e) { return gendef(e); }, exp.u); } - template M::Value *gendef(Ev::Expr const &exp) { + template M::Value gendef(Ev::Expr const &exp) { return gen(exp); } - template M::Value *genval(Ev::Expr const &exp) { + template M::Value genval(Ev::Expr const &exp) { return std::visit([&](const auto &e) { return genval(e); }, exp.u); } template - M::Value *genval(Ev::Expr> const &exp) { - auto *result{std::visit([&](const auto &e) { return genval(e); }, exp.u)}; + M::Value genval(Ev::Expr> const &exp) { + auto result{std::visit([&](const auto &e) { return genval(e); }, exp.u)}; // Handle the `i1` to `fir.logical` conversions as needed. if (result) { M::Type type{result->getType()}; @@ -761,20 +751,18 @@ class ExprLowering { return result; } - template M::Value *gendef(const A &) { + template M::Value gendef(const A &) { assert(false && "expression error"); return {}; } std::string applyNameMangling(const Ev::ProcedureDesignator &proc) { - if (const auto *symbol{proc.GetSymbol()}) { + if (const auto *symbol{proc.GetSymbol()}) return converter.mangleName(*symbol); - } else { - // Do not mangle intrinsic for now - assert(proc.GetSpecificIntrinsic() && - "expected intrinsic procedure in designator"); - return proc.GetName(); - } + // Do not mangle intrinsic for now + assert(proc.GetSpecificIntrinsic() && + "expected intrinsic procedure in designator"); + return proc.GetName(); } public: @@ -785,37 +773,37 @@ class ExprLowering { expr{vop}, symMap{map}, intrinsics{intr}, genLogicalAsI1{logicalAsI1} {} /// Lower the expression `expr` into MLIR standard dialect - M::Value *gen() { return gen(expr); } - M::Value *genval() { return genval(expr); } + M::Value gen() { return gen(expr); } + M::Value genval() { return genval(expr); } }; } // namespace -M::Value *Br::createSomeExpression(M::Location loc, - Br::AbstractConverter &converter, Ev::Expr const &expr, - SymMap &symMap, IntrinsicLibrary const &intrinsics) { +M::Value Br::createSomeExpression(M::Location loc, + Br::AbstractConverter &converter, const Ev::Expr &expr, + SymMap &symMap, const IntrinsicLibrary &intrinsics) { return ExprLowering{loc, converter, expr, symMap, intrinsics, false}.genval(); } -M::Value *Br::createI1LogicalExpression(M::Location loc, - Br::AbstractConverter &converter, Ev::Expr const &expr, - SymMap &symMap, IntrinsicLibrary const &intrinsics) { +M::Value Br::createI1LogicalExpression(M::Location loc, + Br::AbstractConverter &converter, const Ev::Expr &expr, + SymMap &symMap, const IntrinsicLibrary &intrinsics) { return ExprLowering{loc, converter, expr, symMap, intrinsics, true}.genval(); } -M::Value *Br::createSomeAddress(M::Location loc, - Br::AbstractConverter &converter, Ev::Expr const &expr, - SymMap &symMap, IntrinsicLibrary const &intrinsics) { +M::Value Br::createSomeAddress(M::Location loc, + Br::AbstractConverter &converter, const Ev::Expr &expr, + SymMap &symMap, const IntrinsicLibrary &intrinsics) { return ExprLowering{loc, converter, expr, symMap, intrinsics}.gen(); } /// Create a temporary variable /// `symbol` will be nullptr for an anonymous temporary -M::Value *Br::createTemporary(M::Location loc, M::OpBuilder &builder, - SymMap &symMap, M::Type type, Se::Symbol const *symbol) { +M::Value Br::createTemporary(M::Location loc, M::OpBuilder &builder, + SymMap &symMap, M::Type type, const Se::Symbol *symbol) { if (symbol) - if (auto *val{symMap.lookupSymbol(*symbol)}) { - if (auto *op{val->getDefiningOp()}) { + if (auto val = symMap.lookupSymbol(*symbol)) { + if (auto op = val->getDefiningOp()) { return op->getResult(0); } return val; diff --git a/lib/burnside/convert-expr.h b/lib/burnside/convert-expr.h index 468e9c6a068d..1904cff82752 100644 --- a/lib/burnside/convert-expr.h +++ b/lib/burnside/convert-expr.h @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/burnside/convert-expr.h -----------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #ifndef FORTRAN_BURNSIDE_CONVERT_EXPR_H_ #define FORTRAN_BURNSIDE_CONVERT_EXPR_H_ @@ -34,10 +28,12 @@ namespace Fortran { namespace common { class IntrinsicTypeDefaultKinds; } // common + namespace evaluate { template class Expr; struct SomeType; } // evaluate + namespace semantics { class Symbol; } // semantics @@ -47,21 +43,21 @@ namespace burnside { class AbstractConverter; class SymMap; -mlir::Value *createSomeExpression(mlir::Location loc, +mlir::Value createSomeExpression(mlir::Location loc, AbstractConverter &converter, const evaluate::Expr &expr, SymMap &symMap, const IntrinsicLibrary &intrinsics); -mlir::Value *createI1LogicalExpression(mlir::Location loc, +mlir::Value createI1LogicalExpression(mlir::Location loc, AbstractConverter &converter, const evaluate::Expr &expr, SymMap &symMap, const IntrinsicLibrary &intrinsics); -mlir::Value *createSomeAddress(mlir::Location loc, AbstractConverter &converter, +mlir::Value createSomeAddress(mlir::Location loc, AbstractConverter &converter, const evaluate::Expr &expr, SymMap &symMap, const IntrinsicLibrary &intrinsics); -mlir::Value *createTemporary(mlir::Location loc, mlir::OpBuilder &builder, +mlir::Value createTemporary(mlir::Location loc, mlir::OpBuilder &builder, SymMap &symMap, mlir::Type type, const semantics::Symbol *symbol); } // burnside diff --git a/lib/burnside/intrinsics.cc b/lib/burnside/intrinsics.cc index e0d32015a76b..5d981160d4b2 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/burnside/intrinsics.cc @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/burnside/intrinsics.cc ------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #include "intrinsics.h" #include "builder.h" @@ -28,52 +22,55 @@ /// [Coding style](https://llvm.org/docs/CodingStandards.html) +namespace L = llvm; +namespace M = mlir; + namespace Fortran::burnside { /// MathRuntimeLibrary maps Fortran generic intrinsic names to runtime function /// signatures. There is no guarantee that that runtime functions are available /// for all intrinsic functions and possible types. /// To be easy and fast to use, this class holds a map and uses -/// mlir::FunctionType to represent the runtime function type. This imply that +/// M::FunctionType to represent the runtime function type. This imply that /// MathRuntimeLibrary cannot be constexpr built and requires an -/// mlir::MLIRContext to be built. Its constructor uses a constexpr table +/// M::MLIRContext to be built. Its constructor uses a constexpr table /// description of the runtime. The runtime functions are not declared into the -/// mlir::module until there is a query that needs them. This is to avoid +/// M::module until there is a query that needs them. This is to avoid /// polluting the FIR/LLVM IR dumps with unused functions. class MathRuntimeLibrary { public: /// The map key are Fortran generic intrinsic names. - using Key = llvm::StringRef; + using Key = L::StringRef; struct Hash { // Need custom hash for this kind of key - size_t operator()(const Key &k) const { return llvm::hash_value(k); } + size_t operator()(const Key &k) const { return L::hash_value(k); } }; /// Runtime function description that is sufficient to build an - /// mlir::FuncOp and to compare function types. + /// M::FuncOp and to compare function types. struct RuntimeFunction { - RuntimeFunction(llvm::StringRef n, mlir::FunctionType t) - : symbol{n}, type{t} {}; - llvm::StringRef symbol; - mlir::FunctionType type; + RuntimeFunction(L::StringRef n, M::FunctionType t) : symbol{n}, type{t} {}; + L::StringRef symbol; + M::FunctionType type; }; using Map = std::unordered_multimap; - MathRuntimeLibrary(IntrinsicLibrary::Version, mlir::MLIRContext &); + MathRuntimeLibrary(IntrinsicLibrary::Version, M::MLIRContext &); /// Probe the intrinsic library for a certain intrinsic and get/build the - /// related mlir::FuncOp if a runtime description is found. + /// related M::FuncOp if a runtime description is found. /// Also add a unit attribute "fir.runtime" to the function so that later /// it is possible to quickly know what function are intrinsics vs users. - llvm::Optional getFunction( - mlir::OpBuilder &, llvm::StringRef, mlir::FunctionType) const; + L::Optional getFunction( + M::OpBuilder &, L::StringRef, M::FunctionType) const; private: - mlir::FuncOp getFuncOp( - mlir::OpBuilder &builder, const RuntimeFunction &runtime) const; + M::FuncOp getFuncOp( + M::OpBuilder &builder, const RuntimeFunction &runtime) const; Map library; }; /// Enums used to templatize and share lowering of MIN and MAX. enum class Extremum { Min, Max }; + // There are different ways to deal with NaNs in MIN and MAX. // Known existing behaviors are listed below and can be selected for // f18 MIN/MAX implementation. @@ -132,25 +129,22 @@ enum class ExtremumBehavior { // TODO error handling -> return a code or directly emit messages ? class IntrinsicLibrary::Implementation { public: - Implementation(Version v, mlir::MLIRContext &c) : runtime{v, c} {} - inline mlir::Value *genval(mlir::Location loc, mlir::OpBuilder &builder, - llvm::StringRef name, mlir::Type resultType, - llvm::ArrayRef args) const; + Implementation(Version v, M::MLIRContext &c) : runtime{v, c} {} + inline M::Value genval(M::Location loc, M::OpBuilder &builder, + L::StringRef name, M::Type resultType, L::ArrayRef args); private: // Info needed by Generators is passed in Context struct to keep Generator // signatures modification easy. struct Context { - mlir::Location loc; - mlir::OpBuilder *builder{nullptr}; - llvm::StringRef name; - llvm::ArrayRef arguments; - mlir::FunctionType funcType; - mlir::ModuleOp getModuleOp() { return getModule(builder); } - mlir::MLIRContext *getMLIRContext() { - return getModule(builder).getContext(); - } - mlir::Type getResultType() { + M::Location loc; + M::OpBuilder *builder{nullptr}; + L::StringRef name; + L::ArrayRef arguments; + M::FunctionType funcType; + M::ModuleOp getModuleOp() { return getModule(builder); } + M::MLIRContext *getMLIRContext() { return getModule(builder).getContext(); } + M::Type getResultType() { assert(funcType.getNumResults() == 1); return funcType.getResult(0); } @@ -158,7 +152,8 @@ class IntrinsicLibrary::Implementation { /// Define the different FIR generators that can be mapped to intrinsic to /// generate the related code. - using Generator = mlir::Value *(Implementation::*)(Context &)const; + using Generator = M::Value (*)(Context &, MathRuntimeLibrary &); + /// Search a runtime function that is associated to the generic intrinsic name /// and whose signature matches the intrinsic arguments and result types. /// If no such runtime function is found but a runtime function associated @@ -166,23 +161,27 @@ class IntrinsicLibrary::Implementation { /// conversions will be inserted before and/or after the call. This is to /// mainly to allow 16 bits float support even-though little or no math /// runtime is currently available for it. - mlir::Value *genRuntimeCall(Context &) const; + static M::Value genRuntimeCall(Context &, MathRuntimeLibrary &); + /// All generators can be combined with genWrapperCall that will build a /// function named "fir."+ + "." + and /// generate the intrinsic implementation inside instead of at the intrinsic /// call sites. This can be used to keep the FIR more readable. - template mlir::Value *genWrapperCall(Context &c) const { - return outlineInWrapper(g, c); + template + static M::Value genWrapperCall(Context &c, MathRuntimeLibrary &r) { + return outlineInWrapper(g, c, r); } + /// The defaultGenerator is always attempted if no mapping was found for the /// generic name provided. - mlir::Value *defaultGenerator(Context &c) const { - return genWrapperCall<&I::genRuntimeCall>(c); + static M::Value defaultGenerator(Context &c, MathRuntimeLibrary &r) { + return genWrapperCall<&I::genRuntimeCall>(c, r); } - mlir::Value *genConjg(Context &) const; + + static M::Value genConjg(Context &, MathRuntimeLibrary &); template - mlir::Value *genExtremum(Context &) const; - mlir::Value *genMerge(Context &) const; + static M::Value genExtremum(Context &, MathRuntimeLibrary &); + static M::Value genMerge(Context &, MathRuntimeLibrary &); struct IntrinsicHanlder { const char *name; @@ -201,16 +200,17 @@ class IntrinsicLibrary::Implementation { }; // helpers - mlir::Value *outlineInWrapper(Generator, Context &) const; + static M::Value outlineInWrapper( + Generator, Context &c, MathRuntimeLibrary &r); MathRuntimeLibrary runtime; }; // helpers static std::string getIntrinsicWrapperName( - const llvm::StringRef &intrinsic, mlir::FunctionType funTy); -static mlir::FunctionType getFunctionType(mlir::Type resultType, - llvm::ArrayRef arguments, mlir::OpBuilder &); + const L::StringRef &intrinsic, M::FunctionType funTy); +static M::FunctionType getFunctionType( + M::Type resultType, L::ArrayRef arguments, M::OpBuilder &); /// Define a simple static runtime description that will be transformed into /// RuntimeFunction when building the IntrinsicLibrary. @@ -219,7 +219,7 @@ class MathsRuntimeStaticDescription : public RuntimeStaticDescription { constexpr MathsRuntimeStaticDescription( const char *n, const char *s, MaybeTypeCode r, TypeCodeVector a) : RuntimeStaticDescription{s, r, a}, name{n} {} - llvm::StringRef getName() const { return name; } + L::StringRef getName() const { return name; } private: // Generic math function name @@ -275,7 +275,7 @@ static constexpr MathsRuntimeStaticDescription pgmathPreciseRuntime[] = { // IntrinsicLibrary implementation IntrinsicLibrary IntrinsicLibrary::create( - IntrinsicLibrary::Version v, mlir::MLIRContext &context) { + IntrinsicLibrary::Version v, M::MLIRContext &context) { IntrinsicLibrary lib{}; lib.impl = new Implementation(v, context); return lib; @@ -283,9 +283,8 @@ IntrinsicLibrary IntrinsicLibrary::create( IntrinsicLibrary::~IntrinsicLibrary() { delete impl; } -mlir::Value *IntrinsicLibrary::genval(mlir::Location loc, - mlir::OpBuilder &builder, llvm::StringRef name, mlir::Type resultType, - llvm::ArrayRef args) const { +M::Value IntrinsicLibrary::genval(M::Location loc, M::OpBuilder &builder, + L::StringRef name, M::Type resultType, L::ArrayRef args) const { assert(impl); return impl->genval(loc, builder, name, resultType, args); } @@ -295,7 +294,7 @@ mlir::Value *IntrinsicLibrary::genval(mlir::Location loc, // Create the runtime description for the targeted library version. // So far ignore the version an only load the dummy llvm lib and pgmath precise MathRuntimeLibrary::MathRuntimeLibrary( - IntrinsicLibrary::Version, mlir::MLIRContext &mlirContext) { + IntrinsicLibrary::Version, M::MLIRContext &mlirContext) { for (const MathsRuntimeStaticDescription &func : llvmRuntime) { RuntimeFunction impl{ func.getSymbol(), func.getMLIRFunctionType(&mlirContext)}; @@ -308,10 +307,10 @@ MathRuntimeLibrary::MathRuntimeLibrary( } } -mlir::FuncOp MathRuntimeLibrary::getFuncOp( - mlir::OpBuilder &builder, const RuntimeFunction &runtime) const { +M::FuncOp MathRuntimeLibrary::getFuncOp( + M::OpBuilder &builder, const RuntimeFunction &runtime) const { auto module{getModule(&builder)}; - mlir::FuncOp function{getNamedFunction(module, runtime.symbol)}; + M::FuncOp function{getNamedFunction(module, runtime.symbol)}; if (!function) { function = createFunction(module, runtime.symbol, runtime.type); function.setAttr("fir.runtime", builder.getUnitAttr()); @@ -332,7 +331,7 @@ mlir::FuncOp MathRuntimeLibrary::getFuncOp( class FunctionDistance { public: FunctionDistance() : infinite{true} {} - FunctionDistance(mlir::FunctionType from, mlir::FunctionType to) { + FunctionDistance(M::FunctionType from, M::FunctionType to) { auto nInputs{from.getNumInputs()}; auto nResults{from.getNumResults()}; if (nResults != to.getNumResults() || nInputs != to.getNumInputs()) { @@ -360,7 +359,7 @@ class FunctionDistance { private: enum class Conversion { Forbidden, None, Narrow, Extend }; - void addArgumentDistance(mlir::Type from, mlir::Type to) { + void addArgumentDistance(M::Type from, M::Type to) { switch (conversionBetweenTypes(from, to)) { case Conversion::Forbidden: infinite = true; break; case Conversion::None: break; @@ -368,7 +367,7 @@ class FunctionDistance { case Conversion::Extend: conversions[nonNarrowingArg]++; break; } } - void addResultDistance(mlir::Type from, mlir::Type to) { + void addResultDistance(M::Type from, M::Type to) { switch (conversionBetweenTypes(from, to)) { case Conversion::Forbidden: infinite = true; break; case Conversion::None: break; @@ -376,26 +375,26 @@ class FunctionDistance { case Conversion::Extend: conversions[extendingResult]++; break; } } - // Floating point can be mlir::FloatType or fir::real - static unsigned getFloatingPointWidth(mlir::Type t) { - if (auto f{t.dyn_cast()}) return f.getWidth(); + // Floating point can be M::FloatType or fir::real + static unsigned getFloatingPointWidth(M::Type t) { + if (auto f{t.dyn_cast()}) return f.getWidth(); // FIXME: Get width another way for fir.real/complex - // - use fir/KindMapping.h and LLVM::Type + // - use fir/KindMapping.h and L::Type // - or use evaluate/type.h if (auto r{t.dyn_cast()}) return r.getFKind() * 4; if (auto cplx{t.dyn_cast()}) return cplx.getFKind() * 4; assert(false && "not a floating-point type"); return 0; } - static bool isFloatingPointType(mlir::Type t) { - return t.isa() || t.isa(); + static bool isFloatingPointType(M::Type t) { + return t.isa() || t.isa(); } - static Conversion conversionBetweenTypes(mlir::Type from, mlir::Type to) { + static Conversion conversionBetweenTypes(M::Type from, M::Type to) { if (from == to) { return Conversion::None; } - if (auto fromIntTy{from.dyn_cast()}) { - if (auto toIntTy{to.dyn_cast()}) { + if (auto fromIntTy{from.dyn_cast()}) { + if (auto toIntTy{to.dyn_cast()}) { return fromIntTy.getWidth() > toIntTy.getWidth() ? Conversion::Narrow : Conversion::Extend; } @@ -438,9 +437,8 @@ class FunctionDistance { // Select runtime function that has the smallest distance to the intrinsic // function type and that will not imply narrowing arguments or extending the // result. -llvm::Optional MathRuntimeLibrary::getFunction( - mlir::OpBuilder &builder, llvm::StringRef name, - mlir::FunctionType funcType) const { +L::Optional MathRuntimeLibrary::getFunction( + M::OpBuilder &builder, L::StringRef name, M::FunctionType funcType) const { auto range{library.equal_range(name)}; const RuntimeFunction *bestNearMatch{nullptr}; FunctionDistance bestMatchDistance{}; @@ -460,62 +458,65 @@ llvm::Optional MathRuntimeLibrary::getFunction( assert(!bestMatchDistance.isLoosingPrecision() && "runtime selection looses precision"); return getFuncOp(builder, *bestNearMatch); - } else { - return {}; } + return {}; } // IntrinsicLibrary::Implementation implementation -mlir::Value *IntrinsicLibrary::Implementation::genval(mlir::Location loc, - mlir::OpBuilder &builder, llvm::StringRef name, mlir::Type resultType, - llvm::ArrayRef args) const { +M::Value IntrinsicLibrary::Implementation::genval(M::Location loc, + M::OpBuilder &builder, L::StringRef name, M::Type resultType, + L::ArrayRef args) { Context context{ loc, &builder, name, args, getFunctionType(resultType, args, builder)}; - for (const IntrinsicHanlder &handler : handlers) { + for (auto &handler : handlers) { if (name == handler.name) { assert(handler.generator != nullptr); - return (this->*handler.generator)(context); + return handler.generator(context, runtime); } } // Try the default generator if no special handler was defined for the // intrinsic being called. - return this->defaultGenerator(context); + return defaultGenerator(context, runtime); } -static mlir::FunctionType getFunctionType(mlir::Type resultType, - llvm::ArrayRef arguments, mlir::OpBuilder &builder) { - llvm::SmallVector argumentTypes; - for (const auto &arg : arguments) { +static M::FunctionType getFunctionType(M::Type resultType, + L::ArrayRef arguments, M::OpBuilder &builder) { + L::SmallVector argumentTypes; + for (auto &arg : arguments) { assert(arg != nullptr); // TODO think about optionals argumentTypes.push_back(arg->getType()); } - return mlir::FunctionType::get( + return M::FunctionType::get( argumentTypes, resultType, getModule(&builder).getContext()); } // TODO find nicer type to string infra or move this in a mangling utility // mlir as Type::dump(ostream) methods but it may adds ! -static std::string typeToString(mlir::Type t) { - if (auto i{t.dyn_cast()}) { +static std::string typeToString(M::Type t) { + if (auto i{t.dyn_cast()}) { return "i" + std::to_string(i.getWidth()); - } else if (auto cplx{t.dyn_cast()}) { + } + if (auto cplx{t.dyn_cast()}) { return "z" + std::to_string(cplx.getFKind()); - } else if (auto real{t.dyn_cast()}) { + } + if (auto real{t.dyn_cast()}) { return "r" + std::to_string(real.getFKind()); - } else if (auto f{t.dyn_cast()}) { + } + if (auto f{t.dyn_cast()}) { return "f" + std::to_string(f.getWidth()); - } else if (auto logical{t.dyn_cast()}) { + } + if (auto logical{t.dyn_cast()}) { return "l" + std::to_string(logical.getFKind()); - } else if (auto character{t.dyn_cast()}) { + } + if (auto character{t.dyn_cast()}) { return "c" + std::to_string(character.getFKind()); - } else { - assert(false && "no mangling for type"); - return ""s; } + assert(false && "no mangling for type"); + return ""s; } static std::string getIntrinsicWrapperName( - const llvm::StringRef &intrinsic, mlir::FunctionType funTy) { + const L::StringRef &intrinsic, M::FunctionType funTy) { std::string name{"fir." + intrinsic.str() + "."}; assert(funTy.getNumResults() == 1 && "only function mangling supported"); name += typeToString(funTy.getResult(0)); @@ -525,13 +526,13 @@ static std::string getIntrinsicWrapperName( return name; } -mlir::Value *IntrinsicLibrary::Implementation::outlineInWrapper( - Generator generator, Context &context) const { - mlir::ModuleOp module{getModule(context.builder)}; - mlir::MLIRContext *mlirContext{module.getContext()}; +M::Value IntrinsicLibrary::Implementation::outlineInWrapper( + Generator generator, Context &context, MathRuntimeLibrary &runtime) { + M::ModuleOp module{getModule(context.builder)}; + M::MLIRContext *mlirContext{module.getContext()}; std::string wrapperName{ getIntrinsicWrapperName(context.name, context.funcType)}; - mlir::FuncOp function{getNamedFunction(module, wrapperName)}; + M::FuncOp function{getNamedFunction(module, wrapperName)}; if (!function) { // First time this wrapper is needed, build it. function = createFunction(module, wrapperName, context.funcType); @@ -542,34 +543,34 @@ mlir::Value *IntrinsicLibrary::Implementation::outlineInWrapper( // This new function is not linked to a source file location, only // its calls will be. Context localContext{context}; - auto localBuilder{std::make_unique(function)}; + auto localBuilder{std::make_unique(function)}; localBuilder->setInsertionPointToStart(&function.front()); localContext.builder = &(*localBuilder); - llvm::SmallVector localArguments; - for (mlir::BlockArgument *bArg : function.front().getArguments()) { + L::SmallVector localArguments; + for (M::BlockArgument bArg : function.front().getArguments()) { localArguments.push_back(bArg); } localContext.arguments = localArguments; - localContext.loc = mlir::UnknownLoc::get(mlirContext); + localContext.loc = M::UnknownLoc::get(mlirContext); - mlir::Value *result{(this->*generator)(localContext)}; - localBuilder->create(localContext.loc, result); + M::Value result = generator(localContext, runtime); + localBuilder->create(localContext.loc, result); } else { // Wrapper was already built, ensure it has the sought type assert(function.getType() == context.funcType); } - auto call{context.builder->create( + auto call{context.builder->create( context.loc, function, context.arguments)}; return call.getResult(0); } -mlir::Value *IntrinsicLibrary::Implementation::genRuntimeCall( - Context &context) const { +M::Value IntrinsicLibrary::Implementation::genRuntimeCall( + Context &context, MathRuntimeLibrary &runtime) { // Look up runtime - mlir::FunctionType soughtFuncType{context.funcType}; + M::FunctionType soughtFuncType{context.funcType}; if (auto funcOp{runtime.getFunction( *context.builder, context.name, soughtFuncType)}) { - mlir::FunctionType actualFuncType{funcOp->getType()}; + M::FunctionType actualFuncType{funcOp->getType()}; if (actualFuncType.getNumResults() != soughtFuncType.getNumResults() || actualFuncType.getNumInputs() != soughtFuncType.getNumInputs() || actualFuncType.getNumInputs() != context.arguments.size() || @@ -577,10 +578,10 @@ mlir::Value *IntrinsicLibrary::Implementation::genRuntimeCall( assert(false); // TODO better error handling return nullptr; } - llvm::SmallVector convertedArguments; + L::SmallVector convertedArguments; int i{0}; - for (mlir::Value *arg : context.arguments) { - mlir::Type actualType{actualFuncType.getInput(i)}; + for (M::Value arg : context.arguments) { + M::Type actualType{actualFuncType.getInput(i)}; if (soughtFuncType.getInput(i) != actualType) { auto castedArg{context.builder->create( context.loc, actualType, arg)}; @@ -590,10 +591,10 @@ mlir::Value *IntrinsicLibrary::Implementation::genRuntimeCall( } ++i; } - auto call{context.builder->create( + auto call{context.builder->create( context.loc, *funcOp, convertedArguments)}; - mlir::Type soughtType{soughtFuncType.getResult(0)}; - mlir::Value *res{call.getResult(0)}; + M::Type soughtType{soughtFuncType.getResult(0)}; + M::Value res = call.getResult(0); if (actualFuncType.getResult(0) != soughtType) { auto castedRes{context.builder->create( context.loc, soughtType, res)}; @@ -611,48 +612,48 @@ mlir::Value *IntrinsicLibrary::Implementation::genRuntimeCall( } // CONJG -mlir::Value *IntrinsicLibrary::Implementation::genConjg( - Context &genCtxt) const { +M::Value IntrinsicLibrary::Implementation::genConjg( + Context &genCtxt, MathRuntimeLibrary &) { assert(genCtxt.arguments.size() == 1); - mlir::Type resType{genCtxt.getResultType()}; + M::Type resType{genCtxt.getResultType()}; assert(resType == genCtxt.arguments[0]->getType()); - mlir::OpBuilder &builder{*genCtxt.builder}; + M::OpBuilder &builder{*genCtxt.builder}; ComplexHandler cplxHandler{builder, genCtxt.loc}; - mlir::Value *cplx{genCtxt.arguments[0]}; - mlir::Value *imag{cplxHandler.extract(cplx)}; - mlir::Value *negImag{genCtxt.builder->create(genCtxt.loc, imag)}; + M::Value cplx = genCtxt.arguments[0]; + M::Value imag = cplxHandler.extract(cplx); + M::Value negImag = genCtxt.builder->create(genCtxt.loc, imag); return cplxHandler.insert(cplx, negImag); } // MERGE -mlir::Value *IntrinsicLibrary::Implementation::genMerge( - Context &genCtxt) const { +M::Value IntrinsicLibrary::Implementation::genMerge( + Context &genCtxt, MathRuntimeLibrary &) { assert(genCtxt.arguments.size() == 3); - mlir::Type resType{genCtxt.getResultType()}; - mlir::OpBuilder &builder{*genCtxt.builder}; - - auto *trueVal{genCtxt.arguments[0]}; - auto *falseVal{genCtxt.arguments[1]}; - auto *mask{genCtxt.arguments[2]}; - mlir::Type i1Type{mlir::IntegerType::get(1, builder.getContext())}; - mask = builder.create(genCtxt.loc, i1Type, mask); - return builder.create(genCtxt.loc, mask, trueVal, falseVal); + M::Type resType{genCtxt.getResultType()}; + M::OpBuilder &builder{*genCtxt.builder}; + + auto &trueVal = genCtxt.arguments[0]; + auto &falseVal = genCtxt.arguments[1]; + auto &mask = genCtxt.arguments[2]; + M::Type i1Type{M::IntegerType::get(1, builder.getContext())}; + auto msk = builder.create(genCtxt.loc, i1Type, mask); + return builder.create(genCtxt.loc, msk, trueVal, falseVal); } // Compare two FIR values and return boolean result as i1. template -static mlir::Value *createExtremumCompare(mlir::Location loc, - mlir::OpBuilder &builder, mlir::Value *left, mlir::Value *right) { +static M::Value createExtremumCompare( + M::Location loc, M::OpBuilder &builder, M::Value left, M::Value right) { static constexpr auto integerPredicate{extremum == Extremum::Max - ? mlir::CmpIPredicate::sgt - : mlir::CmpIPredicate::slt}; + ? M::CmpIPredicate::sgt + : M::CmpIPredicate::slt}; static constexpr auto orderedCmp{extremum == Extremum::Max ? fir::CmpFPredicate::OGT : fir::CmpFPredicate::OLT}; auto type{left->getType()}; - mlir::Value *result{nullptr}; - if (type.isa() || type.isa()) { + M::Value result; + if (type.isa() || type.isa()) { // Note: the signaling/quit aspect of the result required by IEEE // cannot currently be obtained with LLVM without ad-hoc runtime. if constexpr (behavior == ExtremumBehavior::IeeeMinMaximumNumber) { @@ -662,14 +663,14 @@ static mlir::Value *createExtremumCompare(mlir::Location loc, builder.create(loc, orderedCmp, left, right)}; auto rightIsNan{builder.create( loc, fir::CmpFPredicate::UNE, right, right)}; - result = builder.create(loc, leftIsResult, rightIsNan); + result = builder.create(loc, leftIsResult, rightIsNan); } else if constexpr (behavior == ExtremumBehavior::IeeeMinMaximum) { // Always return NaNs if one the input is NaNs auto leftIsResult{ builder.create(loc, orderedCmp, left, right)}; auto leftIsNan{builder.create( loc, fir::CmpFPredicate::UNE, left, left)}; - result = builder.create(loc, leftIsResult, leftIsNan); + result = builder.create(loc, leftIsResult, leftIsNan); } else if constexpr (behavior == ExtremumBehavior::MinMaxss) { // If the left is a NaN, return the right whatever it is. result = builder.create(loc, orderedCmp, left, right); @@ -684,8 +685,8 @@ static mlir::Value *createExtremumCompare(mlir::Location loc, static_assert(behavior == ExtremumBehavior::IeeeMinMaxNum, "ieeeMinNum/ieeMaxNum behavior not implemented"); } - } else if (type.isa()) { - result = builder.create(loc, integerPredicate, left, right); + } else if (type.isa()) { + result = builder.create(loc, integerPredicate, left, right); } else if (type.isa()) { // TODO: ! character min and max is tricky because the result // length is the length of the longest argument! @@ -697,16 +698,16 @@ static mlir::Value *createExtremumCompare(mlir::Location loc, // MIN and MAX template -mlir::Value *IntrinsicLibrary::Implementation::genExtremum( - Context &genCtxt) const { +M::Value IntrinsicLibrary::Implementation::genExtremum( + Context &genCtxt, MathRuntimeLibrary &) { auto &builder{*genCtxt.builder}; auto loc{genCtxt.loc}; assert(genCtxt.arguments.size() >= 2); - auto *result{genCtxt.arguments[0]}; - for (auto *arg : genCtxt.arguments.drop_front()) { + M::Value result = genCtxt.arguments[0]; + for (auto arg : genCtxt.arguments.drop_front()) { auto mask{ createExtremumCompare(loc, builder, result, arg)}; - result = builder.create(loc, mask, result, arg); + result = builder.create(loc, mask, result, arg); } return result; } diff --git a/lib/burnside/intrinsics.h b/lib/burnside/intrinsics.h index d2534b71d1e3..ae72852602dc 100644 --- a/lib/burnside/intrinsics.h +++ b/lib/burnside/intrinsics.h @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/burnside/intrinsics.h -------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #ifndef FORTRAN_BURNSIDE_INTRINSICS_H_ #define FORTRAN_BURNSIDE_INTRINSICS_H_ @@ -28,7 +22,7 @@ namespace Fortran::burnside { /// that it can be used at different lowering level if needed. /// IntrinsicLibrary is not in charge of generating code for the argument /// expressions/symbols. These must be generated before and the resulting -/// mlir::Values* are inputs for the IntrinsicLibrary operation generation. +/// mlir::Values are inputs for the IntrinsicLibrary operation generation. /// /// The operations generated can be as simple as a single runtime library call /// or they may fully implement the intrinsic without runtime help. This @@ -46,17 +40,17 @@ class IntrinsicLibrary { ~IntrinsicLibrary(); /// Generate the FIR+MLIR operations for the generic intrinsic "name". - /// On failure, returns a nullptr, else the returned mlir::Value* is + /// On failure, returns a nullptr, else the returned mlir::Value is /// the returned Fortran intrinsic value. - mlir::Value *genval(mlir::Location loc, mlir::OpBuilder &builder, + mlir::Value genval(mlir::Location loc, mlir::OpBuilder &builder, llvm::StringRef name, mlir::Type resultType, - llvm::ArrayRef args) const; + llvm::ArrayRef args) const; // TODO: Expose interface to get specific intrinsic function address. // TODO: Handle intrinsic subroutine. // TODO: Intrinsics that do not require their arguments to be defined // (e.g shape inquiries) might not fit in the current interface that - // requires mlir::Value* to be provided. + // requires mlir::Value to be provided. // TODO: Error handling interface ? // TODO: Implementation is incomplete. Many intrinsics to tbd. diff --git a/lib/burnside/io.cc b/lib/burnside/io.cc index 3d4a847bf06d..392b06dbec93 100644 --- a/lib/burnside/io.cc +++ b/lib/burnside/io.cc @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/burnside/io.cc --------------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #include "io.h" #include "bridge.h" @@ -118,22 +112,22 @@ void lowerPrintStatement( // Initiate io M::Type externalUnitType{M::IntegerType::get(32, mlirContext)}; - M::Value *defaultUnit{builder.create( + M::Value defaultUnit{builder.create( loc, builder.getIntegerAttr(externalUnitType, 1))}; - llvm::SmallVector beginArgs{defaultUnit}; - M::Value *cookie{ + llvm::SmallVector beginArgs{defaultUnit}; + M::Value cookie{ builder.create(loc, beginFunc, beginArgs).getResult(0)}; // Call data transfer runtime function - for (M::Value *arg : args) { - llvm::SmallVector operands{cookie, arg}; + for (M::Value arg : args) { + llvm::SmallVector operands{cookie, arg}; M::FuncOp outputFunc{getOutputRuntimeFunction(builder, arg->getType())}; builder.create(loc, outputFunc, operands); } // Terminate IO M::FuncOp endIOFunc{getIORuntimeFunction(builder)}; - llvm::SmallVector endArgs{cookie}; + llvm::SmallVector endArgs{cookie}; builder.create(loc, endIOFunc, endArgs); } @@ -165,7 +159,7 @@ void Br::genOpenStatement(AbstractConverter &, const Pa::OpenStmt &) { void Br::genPrintStatement( Br::AbstractConverter &converter, const Pa::PrintStmt &stmt) { - llvm::SmallVector args; + llvm::SmallVector args; for (auto &item : std::get>(stmt.t)) { if (auto *pe{std::get_if(&item.u)}) { auto loc{converter.genLocation(pe->source)}; diff --git a/lib/burnside/io.h b/lib/burnside/io.h index 8a425529ca79..1cb09d7daa32 100644 --- a/lib/burnside/io.h +++ b/lib/burnside/io.h @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/burnside/io.h ---------------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #ifndef FORTRAN_BURNSIDE_IO_H_ #define FORTRAN_BURNSIDE_IO_H_ @@ -40,7 +34,7 @@ struct WriteStmt; /// design. /// FIXME This interface is also not final. Should it be based on parser::.. /// nodes and lower expressions as needed or should it get every expression -/// already lowered as mlir::Value* ? (currently second options, not sure it +/// already lowered as mlir::Value? (currently second options, not sure it /// will provide enough information for complex IO statements). namespace burnside { diff --git a/lib/burnside/runtime.h b/lib/burnside/runtime.h index c8d77126fb27..b919f4640ef0 100644 --- a/lib/burnside/runtime.h +++ b/lib/burnside/runtime.h @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/burnside/runtime.h ----------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #ifndef FORTRAN_BURNSIDE_RUNTIME_H_ #define FORTRAN_BURNSIDE_RUNTIME_H_ @@ -120,11 +114,11 @@ class RuntimeStaticDescription { // This is currently here because this was designed to provide // maps over runtime description without the burden of having to // instantiate these maps dynamically and to keep them somewhere. -template class StaticMultimapView { +template class StaticMultimapView { public: - using Key = typename Value::Key; + using Key = typename V::Key; struct Range { - using const_iterator = const Value *; + using const_iterator = const V *; constexpr const_iterator begin() const { return startPtr; } constexpr const_iterator end() const { return endPtr; } constexpr bool empty() const { @@ -133,13 +127,13 @@ template class StaticMultimapView { constexpr std::size_t size() const { return empty() ? 0 : static_cast(endPtr - startPtr); } - const Value *startPtr{nullptr}; - const Value *endPtr{nullptr}; + const V *startPtr{nullptr}; + const V *endPtr{nullptr}; }; using const_iterator = typename Range::const_iterator; template - constexpr StaticMultimapView(const Value (&array)[N]) + constexpr StaticMultimapView(const V (&array)[N]) : range{&array[0], &array[0] + N} {} template constexpr bool verify() { // TODO: sorted @@ -154,7 +148,7 @@ template class StaticMultimapView { // std::equal_range will be constexpr in C++20 only. constexpr Range getRange(const Key &key) const { bool matched{false}; - const Value *start{nullptr}, *end{nullptr}; + const V *start{nullptr}, *end{nullptr}; for (const auto &desc : range) { if (desc.key == key) { if (!matched) { diff --git a/lib/fir/FIRDialect.cpp b/lib/fir/FIRDialect.cpp index a8b869202ba2..c3d3ba580e56 100644 --- a/lib/fir/FIRDialect.cpp +++ b/lib/fir/FIRDialect.cpp @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/fir/FIRDialect.cpp ----------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #include "fir/FIRDialect.h" #include "fir/Attribute.h" @@ -28,7 +22,7 @@ namespace { template void selectBuild(M::OpBuilder *builder, M::OperationState *result, - M::Value *condition, + M::Value condition, llvm::ArrayRef tuples) { result->addOperands(condition); for (auto &tup : tuples) { @@ -39,7 +33,7 @@ void selectBuild(M::OpBuilder *builder, M::OperationState *result, for (auto &tup : tuples) { auto *block{std::get(tup)}; assert(block); - auto blkArgs{std::get>(tup)}; + auto blkArgs{std::get>(tup)}; result->addSuccessor(block, blkArgs); } } diff --git a/lib/fir/FIROps.cpp b/lib/fir/FIROps.cpp index 45d4555b4e41..a6c5aa30eeac 100644 --- a/lib/fir/FIROps.cpp +++ b/lib/fir/FIROps.cpp @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/fir/FIROps.cpp --------------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #include "fir/FIROps.h" #include "fir/Attribute.h" @@ -123,7 +117,7 @@ M::ParseResult parseCallOp(M::OpAsmParser &parser, M::OperationState &result) { } else { auto funcArgs = L::ArrayRef(operands).drop_front(); - L::SmallVector resultArgs( + L::SmallVector resultArgs( result.operands.begin() + (result.operands.empty() ? 0 : 1), result.operands.end()); if (parser.resolveOperand(operands[0], funcType, result.operands) || @@ -160,7 +154,7 @@ fir::CmpFPredicate CmpfOp::getPredicateByName(llvm::StringRef name) { } void buildCmpFOp(Builder *builder, OperationState &result, - CmpFPredicate predicate, Value *lhs, Value *rhs) { + CmpFPredicate predicate, Value lhs, Value rhs) { result.addOperands({lhs, rhs}); result.types.push_back(builder->getI1Type()); result.addAttribute( @@ -253,7 +247,7 @@ M::ParseResult parseCmpfOp(M::OpAsmParser &parser, M::OperationState &result) { // CmpcOp void buildCmpCOp(Builder *builder, OperationState &result, - CmpFPredicate predicate, Value *lhs, Value *rhs) { + CmpFPredicate predicate, Value lhs, Value rhs) { result.addOperands({lhs, rhs}); result.types.push_back(builder->getI1Type()); result.addAttribute( @@ -446,8 +440,8 @@ void LoopOp::build(mlir::Builder *builder, mlir::OperationState &result, assert(false && "not implemented"); } -void LoopOp::build(M::Builder *builder, M::OperationState &result, M::Value *lb, - M::Value *ub, L::ArrayRef step) { +void LoopOp::build(M::Builder *builder, M::OperationState &result, M::Value lb, + M::Value ub, L::ArrayRef step) { if (step.empty()) result.addOperands({lb, ub}); else @@ -495,8 +489,8 @@ M::ParseResult parseLoopOp(M::OpAsmParser &parser, M::OperationState &result) { return M::success(); } -fir::LoopOp getForInductionVarOwner(M::Value *val) { - auto *ivArg = dyn_cast(val); +fir::LoopOp getForInductionVarOwner(M::Value val) { + auto ivArg = val.dyn_cast(); if (!ivArg) return fir::LoopOp(); assert(ivArg->getOwner() && "unlinked block argument"); @@ -519,7 +513,7 @@ M::Type StoreOp::elementType(M::Type refType) { // WhereOp void WhereOp::build(M::Builder *builder, M::OperationState &result, - M::Value *cond, bool withElseRegion) { + M::Value cond, bool withElseRegion) { result.addOperands(cond); M::Region *thenRegion = result.addRegion(); M::Region *elseRegion = result.addRegion(); diff --git a/lib/fir/StdConverter.cpp b/lib/fir/StdConverter.cpp index bb7d9cf37a4d..d211c2dcae75 100644 --- a/lib/fir/StdConverter.cpp +++ b/lib/fir/StdConverter.cpp @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/fir/StdConverter.cpp --------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #include "fir/Transforms/StdConverter.h" #include "fir/Attribute.h" @@ -40,8 +34,8 @@ static L::cl::opt namespace { -using SmallVecResult = L::SmallVector; -using OperandTy = L::ArrayRef; +using SmallVecResult = L::SmallVector; +using OperandTy = L::ArrayRef; using AttributeTy = L::ArrayRef; /// FIR to standard type converter @@ -143,17 +137,17 @@ struct SelectTypeOpConversion : public FIROpConversion { } static void genTypeLadderStep(M::Location loc, bool exactTest, - M::Value *selector, M::Type ty, M::Block *dest, + M::Value selector, M::Type ty, M::Block *dest, OperandTy destOps, M::ModuleOp module, M::ConversionPatternRewriter &rewriter) { M::Type tydesc = fir::TypeDescType::get(ty); auto tyattr = M::TypeAttr::get(ty); - M::Value *t = rewriter.create(loc, tydesc, tyattr); + M::Value t = rewriter.create(loc, tydesc, tyattr); M::Type selty = fir::BoxType::get(rewriter.getNoneType()); - M::Value *csel = rewriter.create(loc, selty, selector); + M::Value csel = rewriter.create(loc, selty, selector); M::Type tty = fir::ReferenceType::get(rewriter.getNoneType()); - M::Value *ct = rewriter.create(loc, tty, t); - std::vector actuals = {csel, ct}; + M::Value ct = rewriter.create(loc, tty, t); + std::vector actuals = {csel, ct}; auto fty = rewriter.getI1Type(); std::vector argTy = {selty, tty}; L::StringRef funName = diff --git a/lib/fir/Tilikum.cpp b/lib/fir/Tilikum.cpp index 889231491194..c02fa34b3122 100644 --- a/lib/fir/Tilikum.cpp +++ b/lib/fir/Tilikum.cpp @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/fir/Tilikum.cpp -------------------------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #include "fir/Tilikum/Tilikum.h" #include "fir/Attribute.h" @@ -65,8 +59,8 @@ using namespace fir; namespace { -using SmallVecResult = L::SmallVector; -using OperandTy = L::ArrayRef; +using SmallVecResult = L::SmallVector; +using OperandTy = L::ArrayRef; using AttributeTy = L::ArrayRef; const unsigned defaultAlign = 8; @@ -420,8 +414,8 @@ struct AllocaOpConversion : public FIROpConversion { auto loc = alloc.getLoc(); auto ity = lowering.indexType(); auto c1 = genConstantIndex(loc, ity, rewriter, 1); - auto *size = c1.getResult(); - for (auto *opnd : operands) + auto size = c1.getResult(); + for (auto opnd : operands) size = rewriter.create(loc, ity, size, opnd); auto ty = convertType(alloc.getType()); rewriter.replaceOpWithNewOp(alloc, ty, size, @@ -458,11 +452,11 @@ struct AllocMemOpConversion : public FIROpConversion { auto loc = heap.getLoc(); auto ity = lowering.indexType(); auto c1 = genConstantIndex(loc, ity, rewriter, 1); - auto *size = c1.getResult(); - for (auto *opnd : operands) + auto size = c1.getResult(); + for (auto opnd : operands) size = rewriter.create(loc, ity, size, opnd); heap.setAttr("callee", rewriter.getSymbolRefAttr(mallocFunc)); - L::SmallVector args{size}; + L::SmallVector args{size}; rewriter.replaceOpWithNewOp(heap, ty, args, heap.getAttrs()); return matchSuccess(); @@ -499,16 +493,16 @@ struct FreeMemOpConversion : public FIROpConversion { freemem.setAttr("callee", rewriter.getSymbolRefAttr(freeFunc)); rewriter.replaceOpWithNewOp( freemem, M::LLVM::LLVMType::getVoidTy(dialect), - L::SmallVector{bitcast}, freemem.getAttrs()); + L::SmallVector{bitcast}, freemem.getAttrs()); return matchSuccess(); } }; template M::LLVM::GEPOp genGEP(M::Location loc, M::LLVM::LLVMType ty, - M::ConversionPatternRewriter &rewriter, M::Value *base, + M::ConversionPatternRewriter &rewriter, M::Value base, ARGS... args) { - L::SmallVector cv{args...}; + L::SmallVector cv{args...}; return rewriter.create(loc, ty, base, cv); } @@ -576,9 +570,9 @@ struct BoxDimsOpConversion : public FIROpConversion { return matchSuccess(); } - M::LLVM::LoadOp loadFromOffset(BoxDimsOp boxdims, M::Location loc, - M::Value *a, M::LLVM::ConstantOp c0, - M::LLVM::ConstantOp c7, M::Value *dim, int off, + M::LLVM::LoadOp loadFromOffset(BoxDimsOp boxdims, M::Location loc, M::Value a, + M::LLVM::ConstantOp c0, M::LLVM::ConstantOp c7, + M::Value dim, int off, M::ConversionPatternRewriter &rewriter) const { auto ty = convertType(boxdims.getResult(off)->getType()); auto pty = unwrap(ty).getPointerTo(); @@ -662,7 +656,7 @@ struct BoxIsPtrOpConversion : public FIROpConversion { auto ity = lowering.offsetType(); auto c0 = genConstantOffset(loc, rewriter, 0); auto c5 = genConstantOffset(loc, rewriter, 5); - L::SmallVector args{a, c0, c5}; + L::SmallVector args{a, c0, c5}; auto p = rewriter.create(loc, ty, args); auto ld = rewriter.create(loc, ty, p); auto ab = genConstantOffset(loc, rewriter, 1); @@ -702,7 +696,7 @@ struct BoxRankOpConversion : public FIROpConversion { auto ty = convertType(boxrank.getType()); auto c0 = genConstantOffset(loc, rewriter, 0); auto c3 = genConstantOffset(loc, rewriter, 3); - L::SmallVector args{a, c0, c3}; + L::SmallVector args{a, c0, c3}; auto pty = unwrap(ty).getPointerTo(); auto p = rewriter.create(loc, pty, args); rewriter.replaceOpWithNewOp(boxrank, ty, p); @@ -722,7 +716,7 @@ struct BoxTypeDescOpConversion : public FIROpConversion { auto ty = convertType(boxtypedesc.getType()); auto c0 = genConstantOffset(loc, rewriter, 0); auto c4 = genConstantOffset(loc, rewriter, 4); - L::SmallVector args{a, c0, c4}; + L::SmallVector args{a, c0, c4}; auto pty = unwrap(ty).getPointerTo(); auto p = rewriter.create(loc, pty, args); auto ld = rewriter.create(loc, ty, p); @@ -786,16 +780,16 @@ struct CmpcOpConversion : public FIROpConversion { auto ty = convertType(fir::RealType::get(ctxt, kind)); auto loc = cmp.getLoc(); auto pos0 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(0), ctxt); - L::SmallVector rp{ + L::SmallVector rp{ rewriter.create(loc, ty, operands[0], pos0), rewriter.create(loc, ty, operands[1], pos0)}; auto rcp = rewriter.create(loc, ty, rp, cmp.getAttrs()); auto pos1 = M::ArrayAttr::get(rewriter.getI32IntegerAttr(1), ctxt); - L::SmallVector ip{ + L::SmallVector ip{ rewriter.create(loc, ty, operands[0], pos1), rewriter.create(loc, ty, operands[1], pos1)}; auto icp = rewriter.create(loc, ty, ip, cmp.getAttrs()); - L::SmallVector cp{rcp, icp}; + L::SmallVector cp{rcp, icp}; switch (cmp.getPredicate()) { case fir::CmpFPredicate::OEQ: // .EQ. rewriter.replaceOpWithNewOp(cmp, ty, cp); @@ -839,13 +833,13 @@ struct ConvertOpConversion : public FIROpConversion { auto toTy = unwrap(toTy_); auto *fromLLVMTy = fromTy.getUnderlyingType(); auto *toLLVMTy = toTy.getUnderlyingType(); - auto *op0 = operands[0]; + auto &op0 = operands[0]; if (fromLLVMTy == toLLVMTy) { rewriter.replaceOp(convert, op0); return matchSuccess(); } auto loc = convert.getLoc(); - M::Value *v = {}; + M::Value v; if (fromLLVMTy->isFloatingPointTy()) { if (toLLVMTy->isFloatingPointTy()) { unsigned fromBits = fromLLVMTy->getPrimitiveSizeInBits(); @@ -1013,7 +1007,7 @@ struct EmboxOpConversion : public FIROpConversion { M::LLVM::BitcastOp genGEPToField(M::Location loc, M::LLVM::LLVMType ty, M::ConversionPatternRewriter &rewriter, - M::Value *base, M::Value *zero, + M::Value base, M::Value zero, int field) const { auto coff = genConstantOffset(loc, rewriter, field); auto gep = genGEP(loc, ty, rewriter, base, zero, coff); @@ -1046,7 +1040,7 @@ struct EmboxProcOpConversion : public FIROpConversion { /// return true if all `Value`s in `operands` are `ConstantOp`s bool allConstants(OperandTy operands) { - for (auto *opnd : operands) { + for (auto opnd : operands) { if (auto defop = opnd->getDefiningOp()) if (dyn_cast(defop) || dyn_cast(defop)) @@ -1056,7 +1050,7 @@ bool allConstants(OperandTy operands) { return true; } -M::Attribute getValue(M::Value *value) { +M::Attribute getValue(M::Value value) { assert(value->getDefiningOp()); if (auto v = dyn_cast(value->getDefiningOp())) return v.value(); @@ -1116,7 +1110,7 @@ struct InsertValueOpConversion : public FIROpConversion { /// return true if all `Value`s in `operands` are not `FieldIndexOp`s bool noFieldIndexOps(M::Operation::operand_range operands) { - for (auto *opnd : operands) { + for (auto opnd : operands) { if (auto defop = opnd->getDefiningOp()) if (dyn_cast(defop)) return false; @@ -1134,16 +1128,16 @@ struct CoordinateOpConversion : public FIROpConversion { auto coor = M::cast(op); auto ty = convertType(coor.getType()); auto loc = coor.getLoc(); - M::Value *base = operands[0]; + M::Value base = operands[0]; auto c0 = genConstantIndex(loc, lowering.indexType(), rewriter, 0); // The base can be a boxed reference or a raw reference if (auto boxTy = coor.ref()->getType().dyn_cast()) { if (coor.getNumOperands() == 2) { - auto *coorPtr = *coor.coor().begin(); + auto coorPtr = *coor.coor().begin(); auto *s = coorPtr->getDefiningOp(); if (dyn_cast_or_null(s)) { - auto *lenParam = operands[1]; // byte offset + auto &lenParam = operands[1]; // byte offset auto bc = rewriter.create(loc, voidPtrTy(), base); auto uty = unwrap(ty); auto gep = genGEP(loc, uty, rewriter, bc, lenParam); @@ -1158,7 +1152,7 @@ struct CoordinateOpConversion : public FIROpConversion { base = rewriter.create(loc, pty, p); } - L::SmallVector offs{c0}; + L::SmallVector offs{c0}; auto indices = operands.drop_front(1); offs.append(indices.begin(), indices.end()); if (noFieldIndexOps(coor.coor())) { @@ -1170,10 +1164,10 @@ struct CoordinateOpConversion : public FIROpConversion { // lower the field index ops by walking the indices auto bty = coor.ref()->getType().cast(); M::Type baseTy = ReferenceType::get(bty.getEleTy()); - L::SmallVector args{c0}; + L::SmallVector args{c0}; args.append(coor.coor().begin(), coor.coor().end()); - M::Value *retval = base; + M::Value retval = base; assert(offs.size() == args.size() && "must have same arity"); unsigned pos = 0; for (unsigned i = 0, sz = offs.size(); i != sz; ++i) { @@ -1181,7 +1175,7 @@ struct CoordinateOpConversion : public FIROpConversion { if (auto *defop = args[i]->getDefiningOp()) if (auto field = dyn_cast(defop)) { auto memTy = unwrap(convertType(baseTy)).getPointerTo(); - M::Value *gep = retval; + M::Value gep = retval; if (i - pos > 0) gep = genGEP(loc, memTy, rewriter, gep, arguments(offs, pos, i)); auto bc = rewriter.create(loc, voidPtrTy(), gep); @@ -1217,12 +1211,12 @@ struct CoordinateOpConversion : public FIROpConversion { return matchSuccess(); } - L::SmallVector arguments(L::ArrayRef vec, - unsigned s, unsigned e) const { + L::SmallVector arguments(L::ArrayRef vec, unsigned s, + unsigned e) const { return {vec.begin() + s, vec.begin() + e}; } - int64_t getIntValue(M::Value *val) const { + int64_t getIntValue(M::Value val) const { if (val) if (auto *defop = val->getDefiningOp()) if (auto constOp = dyn_cast(defop)) @@ -1436,7 +1430,7 @@ struct NoReassocOpConversion : public FIROpConversion { } }; -void genCaseLadderStep(M::Location loc, M::Value *cmp, M::Block *dest, +void genCaseLadderStep(M::Location loc, M::Value cmp, M::Block *dest, OperandTy destOps, M::ConversionPatternRewriter &rewriter) { auto *thisBlock = rewriter.getInsertionBlock(); @@ -1620,7 +1614,7 @@ struct StoreOpConversion : public FIROpConversion { // cons an extractvalue on a tuple value, returning value at element `x` M::LLVM::ExtractValueOp -genExtractValueWithIndex(M::Location loc, M::Value *tuple, M::LLVM::LLVMType ty, +genExtractValueWithIndex(M::Location loc, M::Value tuple, M::LLVM::LLVMType ty, M::ConversionPatternRewriter &rewriter, M::MLIRContext *ctx, int x) { auto cx = M::ArrayAttr::get(rewriter.getI32IntegerAttr(x), ctx); @@ -1638,11 +1632,11 @@ struct UnboxCharOpConversion : public FIROpConversion { auto unboxchar = M::cast(op); auto *ctx = unboxchar.getContext(); auto loc = unboxchar.getLoc(); - auto *tuple = operands[0]; + auto &tuple = operands[0]; auto ty = unwrap(tuple->getType()); - auto ptr = genExtractValueWithIndex(loc, tuple, ty, rewriter, ctx, 0); - auto len = genExtractValueWithIndex(loc, tuple, ty, rewriter, ctx, 1); - std::vector repls = {ptr, len}; + M::Value ptr = genExtractValueWithIndex(loc, tuple, ty, rewriter, ctx, 0); + M::Value len = genExtractValueWithIndex(loc, tuple, ty, rewriter, ctx, 1); + std::vector repls = {ptr, len}; unboxchar.replaceAllUsesWith(repls); rewriter.eraseOp(unboxchar); return matchSuccess(); @@ -1650,7 +1644,7 @@ struct UnboxCharOpConversion : public FIROpConversion { }; // generate a GEP into a structure and load the element at position `x` -M::LLVM::LoadOp genLoadWithIndex(M::Location loc, M::Value *tuple, +M::LLVM::LoadOp genLoadWithIndex(M::Location loc, M::Value tuple, M::LLVM::LLVMType ty, M::ConversionPatternRewriter &rewriter, M::LLVM::LLVMType oty, M::LLVM::ConstantOp c0, @@ -1671,20 +1665,20 @@ struct UnboxOpConversion : public FIROpConversion { M::ConversionPatternRewriter &rewriter) const override { auto unbox = M::cast(op); auto loc = unbox.getLoc(); - auto *tuple = operands[0]; + auto &tuple = operands[0]; auto ty = unwrap(tuple->getType()); auto oty = lowering.offsetType(); auto c0 = rewriter.create( loc, oty, rewriter.getI32IntegerAttr(0)); - auto ptr = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 0); - auto len = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 1); - auto ver = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 2); - auto rank = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 3); - auto type = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 4); - auto attr = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 5); - auto xtra = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 6); + M::Value ptr = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 0); + M::Value len = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 1); + M::Value ver = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 2); + M::Value rank = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 3); + M::Value type = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 4); + M::Value attr = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 5); + M::Value xtra = genLoadWithIndex(loc, tuple, ty, rewriter, oty, c0, 6); // FIXME: add dims, etc. - std::vector repls = {ptr, len, ver, rank, type, attr, xtra}; + std::vector repls = {ptr, len, ver, rank, type, attr, xtra}; unbox.replaceAllUsesWith(repls); rewriter.eraseOp(unbox); return matchSuccess(); @@ -1701,11 +1695,11 @@ struct UnboxProcOpConversion : public FIROpConversion { auto unboxproc = M::cast(op); auto *ctx = unboxproc.getContext(); auto loc = unboxproc.getLoc(); - auto *tuple = operands[0]; + auto &tuple = operands[0]; auto ty = unwrap(tuple->getType()); - auto ptr = genExtractValueWithIndex(loc, tuple, ty, rewriter, ctx, 0); - auto host = genExtractValueWithIndex(loc, tuple, ty, rewriter, ctx, 1); - std::vector repls = {ptr, host}; + M::Value ptr = genExtractValueWithIndex(loc, tuple, ty, rewriter, ctx, 0); + M::Value host = genExtractValueWithIndex(loc, tuple, ty, rewriter, ctx, 1); + std::vector repls = {ptr, host}; unboxproc.replaceAllUsesWith(repls); rewriter.eraseOp(unboxproc); return matchSuccess(); diff --git a/lib/fir/Transforms/CSE.cpp b/lib/fir/Transforms/CSE.cpp index 2e765dfedb46..a5f8fcd14d73 100644 --- a/lib/fir/Transforms/CSE.cpp +++ b/lib/fir/Transforms/CSE.cpp @@ -1,24 +1,16 @@ -//===- CSE.cpp - Common Sub-expression Elimination ------------------------===// +//===-- lib/fir/Transforms/CSE.cpp ------------------------------*- C++ -*-===// // -// Copyright 2019 The MLIR Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ============================================================================= -// -// This transformation pass performs a simple common sub-expression elimination -// algorithm on operations within a function. +// 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 +/// This transformation pass performs a simple common sub-expression elimination +/// algorithm on operations within a function. +/// +//===----------------------------------------------------------------------===// #include "fir/FIROpsSupport.h" #include "fir/Transforms/Passes.h" @@ -61,7 +53,9 @@ struct SimpleOperationInfo : public llvm::DenseMapInfo { // - Operands unsigned hashOps; if (op->isCommutative()) { - std::vector vec(op->operand_begin(), op->operand_end()); + std::vector vec; + for (auto i = op->operand_begin(), e = op->operand_end(); i != e; ++i) + vec.push_back((*i).getAsOpaquePointer()); llvm::sort(vec.begin(), vec.end()); hashOps = llvm::hash_combine_range(vec.begin(), vec.end()); } else { @@ -94,9 +88,9 @@ struct SimpleOperationInfo : public llvm::DenseMapInfo { return false; // Compare operands. if (lhs->isCommutative()) { - SmallVector lops(lhs->operand_begin(), lhs->operand_end()); + SmallVector lops(lhs->operand_begin(), lhs->operand_end()); llvm::sort(lops.begin(), lops.end()); - SmallVector rops(rhs->operand_begin(), rhs->operand_end()); + SmallVector rops(rhs->operand_begin(), rhs->operand_end()); llvm::sort(rops.begin(), rops.end()); if (!std::equal(lops.begin(), lops.end(), rops.begin())) return false; diff --git a/lib/fir/Transforms/MemToReg.cpp b/lib/fir/Transforms/MemToReg.cpp index f94198fd7674..5e4d550db0dc 100644 --- a/lib/fir/Transforms/MemToReg.cpp +++ b/lib/fir/Transforms/MemToReg.cpp @@ -1,4 +1,4 @@ -//===- MemToReg.cpp - Generalized mem to reg pass for MLIR dialects ---===// +//===-- lib/fir/Transforms/MemToReg.cpp -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -105,7 +105,7 @@ struct AllocaInfo { }; struct RenamePassData { - using ValVector = std::vector; + using ValVector = std::vector; RenamePassData(M::Block *b, M::Block *p, const ValVector &v) : BB(b), Pred(p), Values(v) {} @@ -232,7 +232,7 @@ struct MemToReg : public M::FunctionPass> { } // Otherwise, we *can* safely rewrite this load. - M::Value *replVal = onlyStore.getOperand(0); + M::Value replVal = onlyStore.getOperand(0); // If the replacement value is the load, this must occur in unreachable // code. if (replVal == LI.getResult()) @@ -302,7 +302,7 @@ struct MemToReg : public M::FunctionPass> { // Otherwise, there was a store before this load, the load takes its // value. Note, if the load was marked as nonnull we don't want to lose // that information when we erase it. So we preserve it with an assume. - M::Value *replVal = std::prev(I)->second->getOperand(0); + M::Value replVal = std::prev(I)->second->getOperand(0); // If the replacement value is the load, this must occur in unreachable // code. @@ -318,7 +318,7 @@ struct MemToReg : public M::FunctionPass> { // Remove the (now dead) stores and alloca. while (!AI.use_empty()) { - auto *ae = AI.getResult(); + auto ae = AI.getResult(); for (auto ai = ae->user_begin(), E = ae->user_end(); ai != E; ai++) if (STORE si = M::dyn_cast(*ai)) { si.erase(); @@ -411,8 +411,8 @@ struct MemToReg : public M::FunctionPass> { } template - void initOperands(std::vector &opers, M::Location &&loc, - M::Block *dest, unsigned size, unsigned ai, M::Value *val, + void initOperands(std::vector &opers, M::Location &&loc, + M::Block *dest, unsigned size, unsigned ai, M::Value val, A &&oldOpers) { unsigned i = 0; for (auto v : oldOpers) @@ -429,27 +429,27 @@ struct MemToReg : public M::FunctionPass> { opers[ai] = val; } - static void eraseIfNoUse(M::Value *val) { + static void eraseIfNoUse(M::Value val) { if (val->use_begin() == val->use_end()) { val->getDefiningOp()->erase(); } } /// Set the incoming value on the branch side for the `ai`th block argument - void setParam(M::Block *blk, unsigned ai, M::Value *val, M::Block *target, + void setParam(M::Block *blk, unsigned ai, M::Value val, M::Block *target, unsigned size) { auto *term = blk->getTerminator(); if (auto br = M::dyn_cast(term)) { if (br.getNumOperands() <= ai) { // construct a new BranchOp to replace term - std::vector opers(size); + std::vector opers(size); auto *dest = br.getDest(); builder->setInsertionPoint(term); initOperands(opers, br.getLoc(), dest, size, ai, val, br.getOperands()); builder->create(br.getLoc(), dest, opers); br.erase(); } else { - auto *oldParam = br.getOperand(ai); + auto oldParam = br.getOperand(ai); br.setOperand(ai, val); eraseIfNoUse(oldParam); } @@ -457,40 +457,40 @@ struct MemToReg : public M::FunctionPass> { if (target == cond.getTrueDest()) { if (cond.getNumTrueOperands() <= ai) { // construct a new CondBranchOp to replace term - std::vector opers(size); + std::vector opers(size); auto *dest = cond.getTrueDest(); builder->setInsertionPoint(term); initOperands(opers, cond.getLoc(), dest, size, ai, val, cond.getTrueOperands()); - auto *c = cond.getCondition(); + auto c = cond.getCondition(); auto *othDest = cond.getFalseDest(); - std::vector othOpers(cond.false_operand_begin(), - cond.false_operand_end()); + std::vector othOpers(cond.false_operand_begin(), + cond.false_operand_end()); builder->create(cond.getLoc(), c, dest, opers, othDest, othOpers); cond.erase(); } else { - auto *oldParam = cond.getTrueOperand(ai); + auto oldParam = cond.getTrueOperand(ai); cond.setTrueOperand(ai, val); eraseIfNoUse(oldParam); } } else { if (cond.getNumFalseOperands() <= ai) { // construct a new CondBranchOp to replace term - std::vector opers(size); + std::vector opers(size); auto *dest = cond.getFalseDest(); builder->setInsertionPoint(term); initOperands(opers, cond.getLoc(), dest, size, ai, val, cond.getFalseOperands()); - auto *c = cond.getCondition(); + auto c = cond.getCondition(); auto *othDest = cond.getTrueDest(); - std::vector othOpers(cond.true_operand_begin(), - cond.true_operand_end()); + std::vector othOpers(cond.true_operand_begin(), + cond.true_operand_end()); builder->create(cond.getLoc(), c, othDest, othOpers, dest, opers); cond.erase(); } else { - auto *oldParam = cond.getFalseOperand(ai); + auto oldParam = cond.getFalseOperand(ai); cond.setFalseOperand(ai, val); eraseIfNoUse(oldParam); } @@ -502,7 +502,7 @@ struct MemToReg : public M::FunctionPass> { inline static void addValue(RenamePassData::ValVector &vector, RenamePassData::ValVector::size_type size, - M::Value *value) { + M::Value value) { if (vector.size() < size + 1) vector.resize(size + 1); vector[size] = value; diff --git a/lib/fir/Transforms/RewriteLoop.cpp b/lib/fir/Transforms/RewriteLoop.cpp index 8a06f16df986..548beed9fb3c 100644 --- a/lib/fir/Transforms/RewriteLoop.cpp +++ b/lib/fir/Transforms/RewriteLoop.cpp @@ -1,16 +1,10 @@ -// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +//===-- lib/fir/Transforms/RewriteLoop.cpp ----------------------*- C++ -*-===// // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// 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 // -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//===----------------------------------------------------------------------===// #include "fir/FIRDialect.h" #include "fir/FIROps.h" @@ -99,11 +93,11 @@ class LoopLoopConv : public M::OpRewritePattern { M::PatternMatchResult matchAndRewrite(LoopOp loop, M::PatternRewriter &rewriter) const override { - auto *low = loop.lowerBound(); - auto *high = loop.upperBound(); + auto low = loop.lowerBound(); + auto high = loop.upperBound(); auto optStep = loop.optstep(); auto loc = loop.getLoc(); - M::Value *step; + M::Value step; if (optStep.begin() != optStep.end()) { step = *optStep.begin(); } else { From f95bc6e7b27a969c70df0471309daa47a0b86243 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Mon, 6 Jan 2020 09:34:14 -0800 Subject: [PATCH 085/123] rename file fix merge problems --- ...gling.md => BijectiveInternalNameUniquing.md} | 0 tools/f18/CMakeLists.txt | 16 ++++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) rename documentation/{BijectiveInternalNameMangling.md => BijectiveInternalNameUniquing.md} (100%) diff --git a/documentation/BijectiveInternalNameMangling.md b/documentation/BijectiveInternalNameUniquing.md similarity index 100% rename from documentation/BijectiveInternalNameMangling.md rename to documentation/BijectiveInternalNameUniquing.md diff --git a/tools/f18/CMakeLists.txt b/tools/f18/CMakeLists.txt index c151f05033a0..c7d7e15ea7f8 100644 --- a/tools/f18/CMakeLists.txt +++ b/tools/f18/CMakeLists.txt @@ -53,15 +53,15 @@ foreach(filename ${MODULES}) else() set(depends ${include}/__fortran_builtins.mod) endif() - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/${filename}.mod - COMMAND f18 -fparse-only -fdebug-semantics ${FLANG_SOURCE_DIR}/module/${filename}.f90 - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include" - DEPENDS f18 ${FLANG_SOURCE_DIR}/module/${filename}.f90 + add_custom_command(OUTPUT ${include}/${filename}.mod + COMMAND f18 -fparse-only -fdebug-semantics -I${include} ${FLANG_SOURCE_DIR}/module/${filename}.f90 + WORKING_DIRECTORY ${include} + DEPENDS f18 ${FLANG_SOURCE_DIR}/module/${filename}.f90 ${depends} ) - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/${filename}.f18.mod - COMMAND f18 -fparse-only -fdebug-semantics -module-suffix .f18.mod ${FLANG_SOURCE_DIR}/module/${filename}.f90 - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include" - DEPENDS f18 ${FLANG_SOURCE_DIR}/module/${filename}.f90 + add_custom_command(OUTPUT ${include}/${filename}.f18.mod + DEPENDS ${include}/${filename}.mod + COMMAND ${CMAKE_COMMAND} -E + copy ${include}/${filename}.mod ${include}/${filename}.f18.mod ) list(APPEND MODULE_FILES ${include}/${filename}.mod) list(APPEND MODULE_FILES ${include}/${filename}.f18.mod) From a6581723b126ad77c94be73e9346d3ddf4e88e93 Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Mon, 6 Jan 2020 13:17:32 -0800 Subject: [PATCH 086/123] remove the type conversion to avoid problems. look for a canonical way to do these type rewrites efficiently. --- lib/fir/StdConverter.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/fir/StdConverter.cpp b/lib/fir/StdConverter.cpp index d211c2dcae75..9aec45902e10 100644 --- a/lib/fir/StdConverter.cpp +++ b/lib/fir/StdConverter.cpp @@ -69,12 +69,15 @@ class FIRToStdTypeConverter : public M::TypeConverter { /// Convert some FIR types to MLIR standard dialect types M::Type convertType(M::Type t) override { +#if 0 + // To lower types, we have to convert everything that uses these types... if (auto cplx = t.dyn_cast()) return M::ComplexType::get(kindToRealType(kindMap, cplx.getFKind())); if (auto integer = t.dyn_cast()) return M::IntegerType::get(integer.getFKind() * 8, integer.getContext()); if (auto real = t.dyn_cast()) return kindToRealType(kindMap, real.getFKind()); +#endif return t; } From 8d0b4db7ef478b8adc1a59a81251e0f3bf2a6126 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 6 Jan 2020 17:36:51 -0800 Subject: [PATCH 087/123] rename burnside namespace to lower --- lib/CMakeLists.txt | 2 +- lib/{burnside => lower}/CMakeLists.txt | 6 +++--- lib/{burnside => lower}/ast-builder.cc | 2 +- lib/{burnside => lower}/ast-builder.h | 10 +++++----- lib/{burnside => lower}/bridge.cc | 6 +++--- lib/{burnside => lower}/bridge.h | 12 ++++++------ lib/{burnside => lower}/builder.cc | 6 +++--- lib/{burnside => lower}/builder.h | 12 ++++++------ lib/{burnside => lower}/cfg-builder.h | 6 +++--- lib/{burnside => lower}/complex-handler.h | 10 +++++----- lib/{burnside => lower}/convert-expr.cc | 6 +++--- lib/{burnside => lower}/convert-expr.h | 12 ++++++------ lib/{burnside => lower}/convert-type.cc | 4 ++-- lib/{burnside => lower}/convert-type.h | 10 +++++----- lib/{burnside => lower}/intrinsics.cc | 6 +++--- lib/{burnside => lower}/intrinsics.h | 10 +++++----- lib/{burnside => lower}/io.cc | 6 +++--- lib/{burnside => lower}/io.h | 10 +++++----- lib/{burnside => lower}/mangler.cc | 4 ++-- lib/{burnside => lower}/mangler.h | 10 +++++----- lib/{burnside => lower}/mixin.h | 10 +++++----- lib/{burnside => lower}/runtime.cc | 6 +++--- lib/{burnside => lower}/runtime.def | 0 lib/{burnside => lower}/runtime.h | 12 ++++++------ tools/bbc/CMakeLists.txt | 2 +- tools/bbc/bbc.cpp | 6 +++--- 26 files changed, 93 insertions(+), 93 deletions(-) rename lib/{burnside => lower}/CMakeLists.txt (90%) rename lib/{burnside => lower}/ast-builder.cc (99%) rename lib/{burnside => lower}/ast-builder.h (98%) rename lib/{burnside => lower}/bridge.cc (99%) rename lib/{burnside => lower}/bridge.h (94%) rename lib/{burnside => lower}/builder.cc (93%) rename lib/{burnside => lower}/builder.h (92%) rename lib/{burnside => lower}/cfg-builder.h (98%) rename lib/{burnside => lower}/complex-handler.h (93%) rename lib/{burnside => lower}/convert-expr.cc (99%) rename lib/{burnside => lower}/convert-expr.h (87%) rename lib/{burnside => lower}/convert-type.cc (99%) rename lib/{burnside => lower}/convert-type.h (95%) rename lib/{burnside => lower}/intrinsics.cc (99%) rename lib/{burnside => lower}/intrinsics.h (92%) rename lib/{burnside => lower}/io.cc (97%) rename lib/{burnside => lower}/io.h (90%) rename lib/{burnside => lower}/mangler.cc (97%) rename lib/{burnside => lower}/mangler.h (88%) rename lib/{burnside => lower}/mixin.h (94%) rename lib/{burnside => lower}/runtime.cc (98%) rename lib/{burnside => lower}/runtime.def (100%) rename lib/{burnside => lower}/runtime.h (96%) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 776b6a11f8ff..effbf0a84fa2 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -12,10 +12,10 @@ if ((CMAKE_CXX_COMPILER_ID MATCHES "Clang")) endif() endif() -add_subdirectory(burnside) add_subdirectory(common) add_subdirectory(decimal) add_subdirectory(evaluate) add_subdirectory(fir) +add_subdirectory(lower) add_subdirectory(parser) add_subdirectory(semantics) diff --git a/lib/burnside/CMakeLists.txt b/lib/lower/CMakeLists.txt similarity index 90% rename from lib/burnside/CMakeLists.txt rename to lib/lower/CMakeLists.txt index 1c004e731e96..914abd95d4ef 100644 --- a/lib/burnside/CMakeLists.txt +++ b/lib/lower/CMakeLists.txt @@ -14,7 +14,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error") -add_library(FortranBurnside +add_library(FortranLower ast-builder.cc bridge.cc builder.cc @@ -26,14 +26,14 @@ add_library(FortranBurnside io.cc ) -target_link_libraries(FortranBurnside +target_link_libraries(FortranLower FIR MLIRAffineOps MLIRLLVMIR MLIRStandardOps ) -install (TARGETS FortranBurnside +install (TARGETS FortranLower ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin diff --git a/lib/burnside/ast-builder.cc b/lib/lower/ast-builder.cc similarity index 99% rename from lib/burnside/ast-builder.cc rename to lib/lower/ast-builder.cc index 6627d9272538..460249cce0eb 100644 --- a/lib/burnside/ast-builder.cc +++ b/lib/lower/ast-builder.cc @@ -27,7 +27,7 @@ /// are either statements or constructs, where a construct contains a list of /// evaluations. The resulting AST structure can then be used to create FIR. -namespace Br = Fortran::burnside; +namespace Br = Fortran::lower; namespace Co = Fortran::common; namespace L = llvm; namespace Pa = Fortran::parser; diff --git a/lib/burnside/ast-builder.h b/lib/lower/ast-builder.h similarity index 98% rename from lib/burnside/ast-builder.h rename to lib/lower/ast-builder.h index a29afe61122d..fbb70830e613 100644 --- a/lib/burnside/ast-builder.h +++ b/lib/lower/ast-builder.h @@ -12,14 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FORTRAN_BURNSIDE_AST_BUILDER_H_ -#define FORTRAN_BURNSIDE_AST_BUILDER_H_ +#ifndef FORTRAN_LOWER_AST_BUILDER_H_ +#define FORTRAN_LOWER_AST_BUILDER_H_ #include "../parser/parse-tree.h" #include "../semantics/scope.h" #include "llvm/Support/raw_ostream.h" -namespace Fortran::burnside { +namespace Fortran::lower { namespace AST { struct Evaluation; @@ -327,6 +327,6 @@ void annotateControl(AST::Program &ast); void dumpAST(llvm::raw_ostream &o, AST::Program &ast); -} // namespace burnside +} // namespace lower -#endif // FORTRAN_BURNSIDE_AST_BUILDER_H_ +#endif // FORTRAN_LOWER_AST_BUILDER_H_ diff --git a/lib/burnside/bridge.cc b/lib/lower/bridge.cc similarity index 99% rename from lib/burnside/bridge.cc rename to lib/lower/bridge.cc index b4ca23ce0a89..468201c0d1d0 100644 --- a/lib/burnside/bridge.cc +++ b/lib/lower/bridge.cc @@ -1,4 +1,4 @@ -//===-- lib/burnside/bridge.cc ----------------------------------*- C++ -*-===// +//===-- lib/lower/bridge.cc -------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -28,7 +28,7 @@ #include "mlir/Parser.h" #include "mlir/Target/LLVMIR.h" -namespace Br = Fortran::burnside; +namespace Br = Fortran::lower; namespace Co = Fortran::common; namespace Ev = Fortran::evaluate; namespace L = llvm; @@ -37,7 +37,7 @@ namespace Pa = Fortran::parser; namespace Se = Fortran::semantics; using namespace Fortran; -using namespace Fortran::burnside; +using namespace Fortran::lower; namespace { diff --git a/lib/burnside/bridge.h b/lib/lower/bridge.h similarity index 94% rename from lib/burnside/bridge.h rename to lib/lower/bridge.h index 50f58a119ab6..eab05f4d0123 100644 --- a/lib/burnside/bridge.h +++ b/lib/lower/bridge.h @@ -1,4 +1,4 @@ -//===-- lib/burnside/bridge.h -----------------------------------*- C++ -*-===// +//===-- lib/lower/bridge.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. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef FORTRAN_BURNSIDE_BRIDGE_H_ -#define FORTRAN_BURNSIDE_BRIDGE_H_ +#ifndef FORTRAN_LOWER_BRIDGE_H_ +#define FORTRAN_LOWER_BRIDGE_H_ #include "../common/Fortran.h" #include "mlir/IR/MLIRContext.h" @@ -50,7 +50,7 @@ namespace fir { struct NameUniquer; } -namespace Fortran::burnside { +namespace Fortran::lower { using SomeExpr = evaluate::Expr; using SymbolRef = common::Reference; @@ -142,6 +142,6 @@ class BurnsideBridge { std::unique_ptr module; }; -} // Fortran::burnside +} // Fortran::lower -#endif // FORTRAN_BURNSIDE_BRIDGE_H_ +#endif // FORTRAN_LOWER_BRIDGE_H_ diff --git a/lib/burnside/builder.cc b/lib/lower/builder.cc similarity index 93% rename from lib/burnside/builder.cc rename to lib/lower/builder.cc index 60b1e5f97a96..61ca189974bb 100644 --- a/lib/burnside/builder.cc +++ b/lib/lower/builder.cc @@ -1,4 +1,4 @@ -//===-- lib/burnside/builder.cc ---------------------------------*- C++ -*-===// +//===-- lib/lower/builder.cc ------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -14,13 +14,13 @@ #include "mlir/IR/Module.h" #include "mlir/IR/Value.h" -namespace B = Fortran::burnside; +namespace B = Fortran::lower; namespace Ev = Fortran::evaluate; namespace M = mlir; namespace Se = Fortran::semantics; using namespace Fortran; -using namespace Fortran::burnside; +using namespace Fortran::lower; M::FuncOp B::createFunction(B::AbstractConverter &converter, llvm::StringRef name, M::FunctionType funcTy) { diff --git a/lib/burnside/builder.h b/lib/lower/builder.h similarity index 92% rename from lib/burnside/builder.h rename to lib/lower/builder.h index 39ae62b6c4eb..aa0cb78373fe 100644 --- a/lib/burnside/builder.h +++ b/lib/lower/builder.h @@ -1,4 +1,4 @@ -//===-- lib/burnside/builder.h ----------------------------------*- C++ -*-===// +//===-- lib/lower/builder.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. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef FORTRAN_BURNSIDE_BUILDER_H_ -#define FORTRAN_BURNSIDE_BUILDER_H_ +#ifndef FORTRAN_LOWER_BUILDER_H_ +#define FORTRAN_LOWER_BUILDER_H_ #include "../semantics/symbol.h" #include "llvm/ADT/DenseMap.h" @@ -29,7 +29,7 @@ namespace evaluate { struct ProcedureDesignator; } -namespace burnside { +namespace lower { /// Miscellaneous helper routines for building MLIR /// @@ -91,7 +91,7 @@ mlir::FuncOp createFunction(AbstractConverter &converter, llvm::StringRef name, mlir::FuncOp createFunction( mlir::ModuleOp module, llvm::StringRef name, mlir::FunctionType funcTy); -} // burnside +} // lower } // Fortran -#endif // FORTRAN_BURNSIDE_BUILDER_H_ +#endif // FORTRAN_LOWER_BUILDER_H_ diff --git a/lib/burnside/cfg-builder.h b/lib/lower/cfg-builder.h similarity index 98% rename from lib/burnside/cfg-builder.h rename to lib/lower/cfg-builder.h index 754c4314629b..cbae0da3a08e 100644 --- a/lib/burnside/cfg-builder.h +++ b/lib/lower/cfg-builder.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FORTRAN_BURNSIDE_BRIDGE_CFG_BUILDER_H_ -#define FORTRAN_BURNSIDE_BRIDGE_CFG_BUILDER_H_ +#ifndef FORTRAN_LOWER_BRIDGE_CFG_BUILDER_H_ +#define FORTRAN_LOWER_BRIDGE_CFG_BUILDER_H_ /// Traverse the AST and complete the CFG by drawing the arcs, pruning unused /// potential targets, making implied jumps explicit, etc. @@ -345,4 +345,4 @@ class CfgBuilder { } }; -#endif // FORTRAN_BURNSIDE_BRIDGE_CFG_BUILDER_H_ +#endif // FORTRAN_LOWER_BRIDGE_CFG_BUILDER_H_ diff --git a/lib/burnside/complex-handler.h b/lib/lower/complex-handler.h similarity index 93% rename from lib/burnside/complex-handler.h rename to lib/lower/complex-handler.h index c16c192462d4..cc77d96c40c3 100644 --- a/lib/burnside/complex-handler.h +++ b/lib/lower/complex-handler.h @@ -1,4 +1,4 @@ -//===-- lib/burnside/complex-handler.h --------------------------*- C++ -*-===// +//===-- lib/lower/complex-handler.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. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef FORTRAN_BURNSIDE_COMPLEX_HANDLER_H_ -#define FORTRAN_BURNSIDE_COMPLEX_HANDLER_H_ +#ifndef FORTRAN_LOWER_COMPLEX_HANDLER_H_ +#define FORTRAN_LOWER_COMPLEX_HANDLER_H_ /// [Coding style](https://llvm.org/docs/CodingStandards.html) @@ -15,7 +15,7 @@ #include "fir/FIROps.h" #include "fir/FIRType.h" -namespace Fortran::burnside { +namespace Fortran::lower { /// Provide helpers to generate Complex manipulations in FIR. class ComplexHandler { @@ -99,4 +99,4 @@ class ComplexHandler { }; } -#endif // FORTRAN_BURNSIDE_COMPLEX_HANDLER_H_ +#endif // FORTRAN_LOWER_COMPLEX_HANDLER_H_ diff --git a/lib/burnside/convert-expr.cc b/lib/lower/convert-expr.cc similarity index 99% rename from lib/burnside/convert-expr.cc rename to lib/lower/convert-expr.cc index 1265cb2cb743..da1ed79b8189 100644 --- a/lib/burnside/convert-expr.cc +++ b/lib/lower/convert-expr.cc @@ -1,4 +1,4 @@ -//===-- lib/burnside/convert-expr.cc ----------------------------*- C++ -*-===// +//===-- lib/lower/convert-expr.cc -------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -37,7 +37,7 @@ #include "mlir/Transforms/DialectConversion.h" #include "mlir/Transforms/Passes.h" -namespace Br = Fortran::burnside; +namespace Br = Fortran::lower; namespace Co = Fortran::common; namespace Ev = Fortran::evaluate; namespace L = llvm; @@ -46,7 +46,7 @@ namespace Pa = Fortran::parser; namespace Se = Fortran::semantics; using namespace Fortran; -using namespace Fortran::burnside; +using namespace Fortran::lower; namespace { diff --git a/lib/burnside/convert-expr.h b/lib/lower/convert-expr.h similarity index 87% rename from lib/burnside/convert-expr.h rename to lib/lower/convert-expr.h index 1904cff82752..b1d6469585a4 100644 --- a/lib/burnside/convert-expr.h +++ b/lib/lower/convert-expr.h @@ -1,4 +1,4 @@ -//===-- lib/burnside/convert-expr.h -----------------------------*- C++ -*-===// +//===-- lib/lower/convert-expr.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. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef FORTRAN_BURNSIDE_CONVERT_EXPR_H_ -#define FORTRAN_BURNSIDE_CONVERT_EXPR_H_ +#ifndef FORTRAN_LOWER_CONVERT_EXPR_H_ +#define FORTRAN_LOWER_CONVERT_EXPR_H_ #include "intrinsics.h" @@ -38,7 +38,7 @@ namespace semantics { class Symbol; } // semantics -namespace burnside { +namespace lower { class AbstractConverter; class SymMap; @@ -60,7 +60,7 @@ mlir::Value createSomeAddress(mlir::Location loc, AbstractConverter &converter, mlir::Value createTemporary(mlir::Location loc, mlir::OpBuilder &builder, SymMap &symMap, mlir::Type type, const semantics::Symbol *symbol); -} // burnside +} // lower } // Fortran -#endif // FORTRAN_BURNSIDE_CONVERT_EXPR_H_ +#endif // FORTRAN_LOWER_CONVERT_EXPR_H_ diff --git a/lib/burnside/convert-type.cc b/lib/lower/convert-type.cc similarity index 99% rename from lib/burnside/convert-type.cc rename to lib/lower/convert-type.cc index b82630dc60f3..3ec61b744a7c 100644 --- a/lib/burnside/convert-type.cc +++ b/lib/lower/convert-type.cc @@ -23,7 +23,7 @@ #include "mlir/IR/MLIRContext.h" #include "mlir/IR/StandardTypes.h" -namespace Br = Fortran::burnside; +namespace Br = Fortran::lower; namespace Co = Fortran::common; namespace Ev = Fortran::evaluate; namespace M = mlir; @@ -31,7 +31,7 @@ namespace Pa = Fortran::parser; namespace Se = Fortran::semantics; using namespace Fortran; -using namespace Fortran::burnside; +using namespace Fortran::lower; namespace { diff --git a/lib/burnside/convert-type.h b/lib/lower/convert-type.h similarity index 95% rename from lib/burnside/convert-type.h rename to lib/lower/convert-type.h index a842f0862782..377c7e75dde5 100644 --- a/lib/burnside/convert-type.h +++ b/lib/lower/convert-type.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FORTRAN_BURNSIDE_CONVERT_TYPE_H_ -#define FORTRAN_BURNSIDE_CONVERT_TYPE_H_ +#ifndef FORTRAN_LOWER_CONVERT_TYPE_H_ +#define FORTRAN_LOWER_CONVERT_TYPE_H_ /// Conversion of front-end TYPE, KIND, ATTRIBUTE (TKA) information to FIR/MLIR. /// This is meant to be the single point of truth (SPOT) for all type @@ -56,7 +56,7 @@ namespace semantics { class Symbol; } // semantics -namespace burnside { +namespace lower { using SomeExpr = evaluate::Expr; using SymbolRef = common::Reference; @@ -103,7 +103,7 @@ mlir::FunctionType translateSymbolToFIRFunctionType(mlir::MLIRContext *ctxt, mlir::Type convertReal(mlir::MLIRContext *ctxt, int KIND); -} // burnside +} // lower } // Fortran -#endif // FORTRAN_BURNSIDE_CONVERT_TYPE_H_ +#endif // FORTRAN_LOWER_CONVERT_TYPE_H_ diff --git a/lib/burnside/intrinsics.cc b/lib/lower/intrinsics.cc similarity index 99% rename from lib/burnside/intrinsics.cc rename to lib/lower/intrinsics.cc index 5d981160d4b2..24c22efbf251 100644 --- a/lib/burnside/intrinsics.cc +++ b/lib/lower/intrinsics.cc @@ -1,4 +1,4 @@ -//===-- lib/burnside/intrinsics.cc ------------------------------*- C++ -*-===// +//===-- lib/lower/intrinsics.cc ---------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -25,7 +25,7 @@ namespace L = llvm; namespace M = mlir; -namespace Fortran::burnside { +namespace Fortran::lower { /// MathRuntimeLibrary maps Fortran generic intrinsic names to runtime function /// signatures. There is no guarantee that that runtime functions are available @@ -712,4 +712,4 @@ M::Value IntrinsicLibrary::Implementation::genExtremum( return result; } -} // namespace Fortran::burnside +} // namespace Fortran::lower diff --git a/lib/burnside/intrinsics.h b/lib/lower/intrinsics.h similarity index 92% rename from lib/burnside/intrinsics.h rename to lib/lower/intrinsics.h index ae72852602dc..168dec84d298 100644 --- a/lib/burnside/intrinsics.h +++ b/lib/lower/intrinsics.h @@ -1,4 +1,4 @@ -//===-- lib/burnside/intrinsics.h -------------------------------*- C++ -*-===// +//===-- lib/lower/intrinsics.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. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef FORTRAN_BURNSIDE_INTRINSICS_H_ -#define FORTRAN_BURNSIDE_INTRINSICS_H_ +#ifndef FORTRAN_LOWER_INTRINSICS_H_ +#define FORTRAN_LOWER_INTRINSICS_H_ #include "llvm/ADT/StringRef.h" #include "mlir/Dialect/StandardOps/Ops.h" @@ -15,7 +15,7 @@ /// [Coding style](https://llvm.org/docs/CodingStandards.html) -namespace Fortran::burnside { +namespace Fortran::lower { /// IntrinsicLibrary generates FIR+MLIR operations that implement Fortran /// generic intrinsic function calls. It operates purely on FIR+MLIR types so @@ -65,4 +65,4 @@ class IntrinsicLibrary { } -#endif // FORTRAN_BURNSIDE_INTRINSICS_H_ +#endif // FORTRAN_LOWER_INTRINSICS_H_ diff --git a/lib/burnside/io.cc b/lib/lower/io.cc similarity index 97% rename from lib/burnside/io.cc rename to lib/lower/io.cc index 392b06dbec93..b3d12c9cf230 100644 --- a/lib/burnside/io.cc +++ b/lib/lower/io.cc @@ -1,4 +1,4 @@ -//===-- lib/burnside/io.cc --------------------------------------*- C++ -*-===// +//===-- lib/lower/io.cc -----------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -16,12 +16,12 @@ #include "mlir/IR/Builders.h" #include -namespace Br = Fortran::burnside; +namespace Br = Fortran::lower; namespace M = mlir; namespace Pa = Fortran::parser; using namespace Fortran; -using namespace Fortran::burnside; +using namespace Fortran::lower; namespace { diff --git a/lib/burnside/io.h b/lib/lower/io.h similarity index 90% rename from lib/burnside/io.h rename to lib/lower/io.h index 1cb09d7daa32..3bb95999f36d 100644 --- a/lib/burnside/io.h +++ b/lib/lower/io.h @@ -1,4 +1,4 @@ -//===-- lib/burnside/io.h ---------------------------------------*- C++ -*-===// +//===-- lib/lower/io.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. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef FORTRAN_BURNSIDE_IO_H_ -#define FORTRAN_BURNSIDE_IO_H_ +#ifndef FORTRAN_LOWER_IO_H_ +#define FORTRAN_LOWER_IO_H_ namespace mlir { class OpBuilder; @@ -36,7 +36,7 @@ struct WriteStmt; /// nodes and lower expressions as needed or should it get every expression /// already lowered as mlir::Value? (currently second options, not sure it /// will provide enough information for complex IO statements). -namespace burnside { +namespace lower { class AbstractConverter; class BridgeImpl; @@ -55,4 +55,4 @@ void genWriteStatement(AbstractConverter &, const parser::WriteStmt &); } } -#endif // FORTRAN_BURNSIDE_IO_H_ +#endif // FORTRAN_LOWER_IO_H_ diff --git a/lib/burnside/mangler.cc b/lib/lower/mangler.cc similarity index 97% rename from lib/burnside/mangler.cc rename to lib/lower/mangler.cc index 1cbca9e387d0..1b2e59fb0786 100644 --- a/lib/burnside/mangler.cc +++ b/lib/lower/mangler.cc @@ -22,10 +22,10 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" -namespace Br = Fortran::burnside; +namespace Br = Fortran::lower; namespace Co = Fortran::common; namespace L = llvm; -namespace Ma = Fortran::burnside::mangle; +namespace Ma = Fortran::lower::mangle; namespace Se = Fortran::semantics; using namespace Fortran; diff --git a/lib/burnside/mangler.h b/lib/lower/mangler.h similarity index 88% rename from lib/burnside/mangler.h rename to lib/lower/mangler.h index 11da39c38d4b..7d1691f91af2 100644 --- a/lib/burnside/mangler.h +++ b/lib/lower/mangler.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FORTRAN_BURNSIDE_MANGLER_H_ -#define FORTRAN_BURNSIDE_MANGLER_H_ +#ifndef FORTRAN_LOWER_MANGLER_H_ +#define FORTRAN_LOWER_MANGLER_H_ #include @@ -34,7 +34,7 @@ namespace semantics { class Symbol; } -namespace burnside { +namespace lower { using SymbolRef = common::Reference; namespace mangle { @@ -45,7 +45,7 @@ std::string mangleName(fir::NameUniquer &uniquer, const SymbolRef symbol); std::string demangleName(llvm::StringRef name); } // mangle -} // burnside +} // lower } // Fortran -#endif // FORTRAN_BURNSIDE_MANGLER_H_ +#endif // FORTRAN_LOWER_MANGLER_H_ diff --git a/lib/burnside/mixin.h b/lib/lower/mixin.h similarity index 94% rename from lib/burnside/mixin.h rename to lib/lower/mixin.h index f24ccc2b1b08..0d75f7aaa4ed 100644 --- a/lib/burnside/mixin.h +++ b/lib/lower/mixin.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FORTRAN_BURNSIDE_MIXIN_H_ -#define FORTRAN_BURNSIDE_MIXIN_H_ +#ifndef FORTRAN_LOWER_MIXIN_H_ +#define FORTRAN_LOWER_MIXIN_H_ // Mixin classes are "partial" classes (not used standalone) that can be used to // add a repetitive (ad hoc) interface (and implementation) to a class. It's @@ -28,7 +28,7 @@ #include #include -namespace Fortran::burnside { +namespace Fortran::lower { // implementation of a (moveable) sum type (variant) template struct SumTypeMixin { @@ -67,6 +67,6 @@ template struct SumTypeCopyMixin { return *this; \ } -} // namespace burnside +} // namespace lower -#endif // FORTRAN_BURNSIDE_MIXIN_H_ +#endif // FORTRAN_LOWER_MIXIN_H_ diff --git a/lib/burnside/runtime.cc b/lib/lower/runtime.cc similarity index 98% rename from lib/burnside/runtime.cc rename to lib/lower/runtime.cc index fcf7f9d1d722..415fe48b4afc 100644 --- a/lib/burnside/runtime.cc +++ b/lib/lower/runtime.cc @@ -22,7 +22,7 @@ #include "mlir/IR/Types.h" #include -namespace Fortran::burnside { +namespace Fortran::lower { mlir::Type RuntimeStaticDescription::getMLIRType( TypeCode t, mlir::MLIRContext *context) { switch (t) { @@ -72,9 +72,9 @@ mlir::FuncOp RuntimeStaticDescription::getFuncOp( // TODO remove dependencies to stub rt below -namespace Br = Fortran::burnside; +namespace Br = Fortran::lower; using namespace Fortran; -using namespace Fortran::burnside; +using namespace Fortran::lower; namespace { diff --git a/lib/burnside/runtime.def b/lib/lower/runtime.def similarity index 100% rename from lib/burnside/runtime.def rename to lib/lower/runtime.def diff --git a/lib/burnside/runtime.h b/lib/lower/runtime.h similarity index 96% rename from lib/burnside/runtime.h rename to lib/lower/runtime.h index b919f4640ef0..6477ef74e9c7 100644 --- a/lib/burnside/runtime.h +++ b/lib/lower/runtime.h @@ -1,4 +1,4 @@ -//===-- lib/burnside/runtime.h ----------------------------------*- C++ -*-===// +//===-- lib/lower/runtime.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. @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef FORTRAN_BURNSIDE_RUNTIME_H_ -#define FORTRAN_BURNSIDE_RUNTIME_H_ +#ifndef FORTRAN_LOWER_RUNTIME_H_ +#define FORTRAN_LOWER_RUNTIME_H_ #include @@ -22,7 +22,7 @@ class OpBuilder; class FuncOp; } -namespace Fortran::burnside { +namespace Fortran::lower { /// [Coding style](https://llvm.org/docs/CodingStandards.html) @@ -196,6 +196,6 @@ mlir::FunctionType getRuntimeEntryType( mlir::FunctionType getRuntimeEntryType(RuntimeEntryCode code, mlir::MLIRContext &mlirContext, int inpKind, int resKind); -} // Fortran::burnside +} // Fortran::lower -#endif // FORTRAN_BURNSIDE_RUNTIME_H_ +#endif // FORTRAN_LOWER_RUNTIME_H_ diff --git a/tools/bbc/CMakeLists.txt b/tools/bbc/CMakeLists.txt index f0917485a173..579f6c6e98b9 100644 --- a/tools/bbc/CMakeLists.txt +++ b/tools/bbc/CMakeLists.txt @@ -32,7 +32,7 @@ set(EXE_LIBS FortranParser FortranEvaluate FortranSemantics - FortranBurnside + FortranLower ) add_llvm_tool(bbc bbc.cpp) diff --git a/tools/bbc/bbc.cpp b/tools/bbc/bbc.cpp index 34e9471c0f35..e88df7ad1853 100644 --- a/tools/bbc/bbc.cpp +++ b/tools/bbc/bbc.cpp @@ -14,8 +14,8 @@ // Temporary Fortran front end driver main program for development scaffolding. -#include "../../lib/burnside/bridge.h" -#include "../../lib/burnside/convert-expr.h" +#include "../../lib/lower/bridge.h" +#include "../../lib/lower/convert-expr.h" #include "../../lib/common/Fortran-features.h" #include "../../lib/common/default-kinds.h" #include "../../lib/parser/characters.h" @@ -61,7 +61,7 @@ #include #include -namespace Br = Fortran::burnside; +namespace Br = Fortran::lower; static std::list argList(int argc, char *const argv[]) { std::list result; From bc8ee7f6abd2062b57b37410656c591d62125f0c Mon Sep 17 00:00:00 2001 From: Eric Schweitz Date: Tue, 7 Jan 2020 10:34:28 -0800 Subject: [PATCH 088/123] rename "fir" directories to "optimizer" --- include/CMakeLists.txt | 2 +- include/{fir => optimizer}/.clang-format | 0 .../Analysis/IteratedDominanceFrontier.h | 7 +- include/{fir => optimizer}/Attribute.h | 6 +- include/{fir => optimizer}/CMakeLists.txt | 0 .../Tilikum.h => optimizer/CodeGen/CodeGen.h} | 9 +- include/{fir => optimizer}/FIRDialect.h | 6 +- include/{fir => optimizer}/FIROps.h | 8 +- include/{fir => optimizer}/FIROps.td | 0 include/{fir => optimizer}/FIROpsSupport.h | 8 +- include/{fir => optimizer}/FIRType.h | 6 +- include/{fir => optimizer}/InternalNames.h | 6 +- include/{fir => optimizer}/KindMapping.h | 6 +- .../{fir => optimizer}/Transforms/Passes.h | 6 +- .../Transforms/StdConverter.h | 6 +- lib/CMakeLists.txt | 2 +- lib/{fir => lower}/.clang-format | 0 lib/lower/ast-builder.cc | 122 ++++--- lib/lower/ast-builder.h | 120 +++---- lib/lower/bridge.cc | 309 ++++++++++-------- lib/lower/bridge.h | 36 +- lib/lower/builder.cc | 18 +- lib/lower/builder.h | 14 +- lib/lower/cfg-builder.h | 80 +++-- lib/lower/complex-handler.h | 37 ++- lib/lower/convert-expr.cc | 276 +++++++++------- lib/lower/convert-expr.h | 42 +-- lib/lower/convert-type.cc | 182 +++++++---- lib/lower/convert-type.h | 74 +++-- lib/lower/intrinsics.cc | 237 ++++++++------ lib/lower/intrinsics.h | 12 +- lib/lower/io.cc | 38 +-- lib/lower/io.h | 10 +- lib/lower/mangler.cc | 55 ++-- lib/lower/mangler.h | 11 +- lib/lower/mixin.h | 42 ++- lib/lower/runtime.cc | 56 ++-- lib/lower/runtime.h | 50 +-- lib/optimizer/.clang-format | 2 + lib/{fir => optimizer}/Attribute.cpp | 6 +- lib/{fir => optimizer}/CMakeLists.txt | 2 +- .../Tilikum.cpp => optimizer/CodeGen.cpp} | 16 +- lib/{fir => optimizer}/FIRDialect.cpp | 12 +- lib/{fir => optimizer}/FIROps.cpp | 12 +- lib/{fir => optimizer}/FIRType.cpp | 4 +- lib/{fir => optimizer}/InternalNames.cpp | 2 +- .../IteratedDominanceFrontier.cpp | 2 +- lib/{fir => optimizer}/KindMapping.cpp | 2 +- lib/{fir => optimizer}/StdConverter.cpp | 14 +- .../Transforms/CMakeLists.txt | 0 lib/{fir => optimizer}/Transforms/CSE.cpp | 6 +- .../Transforms/MemToReg.cpp | 10 +- .../Transforms/RewriteLoop.cpp | 6 +- tools/bbc/bbc.cpp | 16 +- tools/tco/.clang-format | 2 + tools/tco/tco.cpp | 31 +- 56 files changed, 1136 insertions(+), 908 deletions(-) rename include/{fir => optimizer}/.clang-format (100%) rename include/{fir => optimizer}/Analysis/IteratedDominanceFrontier.h (97%) rename include/{fir => optimizer}/Attribute.h (97%) rename include/{fir => optimizer}/CMakeLists.txt (100%) rename include/{fir/Tilikum/Tilikum.h => optimizer/CodeGen/CodeGen.h} (82%) rename include/{fir => optimizer}/FIRDialect.h (93%) rename include/{fir => optimizer}/FIROps.h (97%) rename include/{fir => optimizer}/FIROps.td (100%) rename include/{fir => optimizer}/FIROpsSupport.h (95%) rename include/{fir => optimizer}/FIRType.h (99%) rename include/{fir => optimizer}/InternalNames.h (97%) rename include/{fir => optimizer}/KindMapping.h (96%) rename include/{fir => optimizer}/Transforms/Passes.h (92%) rename include/{fir => optimizer}/Transforms/StdConverter.h (86%) rename lib/{fir => lower}/.clang-format (100%) create mode 100644 lib/optimizer/.clang-format rename lib/{fir => optimizer}/Attribute.cpp (98%) rename lib/{fir => optimizer}/CMakeLists.txt (98%) rename lib/{fir/Tilikum.cpp => optimizer/CodeGen.cpp} (99%) rename lib/{fir => optimizer}/FIRDialect.cpp (91%) rename lib/{fir => optimizer}/FIROps.cpp (98%) rename lib/{fir => optimizer}/FIRType.cpp (99%) rename lib/{fir => optimizer}/InternalNames.cpp (99%) rename lib/{fir => optimizer}/IteratedDominanceFrontier.cpp (98%) rename lib/{fir => optimizer}/KindMapping.cpp (99%) rename lib/{fir => optimizer}/StdConverter.cpp (96%) rename lib/{fir => optimizer}/Transforms/CMakeLists.txt (100%) rename lib/{fir => optimizer}/Transforms/CSE.cpp (98%) rename lib/{fir => optimizer}/Transforms/MemToReg.cpp (99%) rename lib/{fir => optimizer}/Transforms/RewriteLoop.cpp (98%) create mode 100644 tools/tco/.clang-format diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index bd3543028a2c..53f754abd9a1 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -1,2 +1,2 @@ -add_subdirectory(fir) add_subdirectory(flang) +add_subdirectory(optimizer) diff --git a/include/fir/.clang-format b/include/optimizer/.clang-format similarity index 100% rename from include/fir/.clang-format rename to include/optimizer/.clang-format diff --git a/include/fir/Analysis/IteratedDominanceFrontier.h b/include/optimizer/Analysis/IteratedDominanceFrontier.h similarity index 97% rename from include/fir/Analysis/IteratedDominanceFrontier.h rename to include/optimizer/Analysis/IteratedDominanceFrontier.h index c8858d0a8f70..0ecdd2d29de2 100644 --- a/include/fir/Analysis/IteratedDominanceFrontier.h +++ b/include/optimizer/Analysis/IteratedDominanceFrontier.h @@ -20,8 +20,8 @@ // //===----------------------------------------------------------------------===// -#ifndef FIR_ANALYSIS_IDF_H -#define FIR_ANALYSIS_IDF_H +#ifndef OPTIMIZER_ANALYSIS_IDF_H +#define OPTIMIZER_ANALYSIS_IDF_H #include "mlir/Analysis/Dominance.h" #include "mlir/IR/Block.h" @@ -94,4 +94,5 @@ class IDFCalculator { typedef IDFCalculator ForwardIDFCalculator; } // namespace fir -#endif + +#endif // OPTIMIZER_ANALYSIS_IDF_H diff --git a/include/fir/Attribute.h b/include/optimizer/Attribute.h similarity index 97% rename from include/fir/Attribute.h rename to include/optimizer/Attribute.h index 9299774dd102..217096c6a33a 100644 --- a/include/fir/Attribute.h +++ b/include/optimizer/Attribute.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef DIALECT_FIR_FIRATTRIBUTE_H -#define DIALECT_FIR_FIRATTRIBUTE_H +#ifndef OPTIMIZER_FIRATTRIBUTE_H +#define OPTIMIZER_FIRATTRIBUTE_H #include "mlir/IR/Attributes.h" @@ -139,4 +139,4 @@ void printFirAttribute(FIROpsDialect *dialect, mlir::Attribute attr, } // namespace fir -#endif // DIALECT_FIR_FIRATTRIBUTE_H +#endif // OPTIMIZER_FIRATTRIBUTE_H diff --git a/include/fir/CMakeLists.txt b/include/optimizer/CMakeLists.txt similarity index 100% rename from include/fir/CMakeLists.txt rename to include/optimizer/CMakeLists.txt diff --git a/include/fir/Tilikum/Tilikum.h b/include/optimizer/CodeGen/CodeGen.h similarity index 82% rename from include/fir/Tilikum/Tilikum.h rename to include/optimizer/CodeGen/CodeGen.h index 691e56f00d13..0b51eff16661 100644 --- a/include/fir/Tilikum/Tilikum.h +++ b/include/optimizer/CodeGen/CodeGen.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIR_TILIKUM_TILIKUM_H -#define FIR_TILIKUM_TILIKUM_H +#ifndef OPTIMIZER_CODEGEN_CODEGEN_H +#define OPTIMIZER_CODEGEN_CODEGEN_H #include @@ -32,9 +32,8 @@ struct NameUniquer; std::unique_ptr createFIRToLLVMPass(NameUniquer &uniquer); /// Convert the LLVM IR dialect to LLVM-IR proper -std::unique_ptr -createLLVMDialectToLLVMPass(llvm::StringRef output); +std::unique_ptr createLLVMDialectToLLVMPass(llvm::StringRef output); } // namespace fir -#endif // FIR_TILIKUM_TILIKUM_H +#endif // OPTIMIZER_CODEGEN_CODEGEN_H diff --git a/include/fir/FIRDialect.h b/include/optimizer/FIRDialect.h similarity index 93% rename from include/fir/FIRDialect.h rename to include/optimizer/FIRDialect.h index 708b81bdadc9..1741ead46eaf 100644 --- a/include/fir/FIRDialect.h +++ b/include/optimizer/FIRDialect.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef DIALECT_FIR_FIRDIALECT_H -#define DIALECT_FIR_FIRDIALECT_H +#ifndef OPTIMIZER_FIRDIALECT_H +#define OPTIMIZER_FIRDIALECT_H #include "mlir/IR/Dialect.h" @@ -52,4 +52,4 @@ class FIROpsDialect final : public mlir::Dialect { } // namespace fir -#endif // DIALECT_FIR_FIRDIALECT_H +#endif // OPTIMIZER_FIRDIALECT_H diff --git a/include/fir/FIROps.h b/include/optimizer/FIROps.h similarity index 97% rename from include/fir/FIROps.h rename to include/optimizer/FIROps.h index 2bf565c1da01..f720ca941a55 100644 --- a/include/fir/FIROps.h +++ b/include/optimizer/FIROps.h @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef FIR_FIROPS_H -#define FIR_FIROPS_H +#ifndef OPTIMIZER_FIROPS_H +#define OPTIMIZER_FIROPS_H #include "mlir/IR/Builders.h" #include "mlir/IR/OpImplementation.h" @@ -124,7 +124,7 @@ ParseResult parseCmpfOp(OpAsmParser &parser, OperationState &result); ParseResult parseCmpcOp(OpAsmParser &parser, OperationState &result); #define GET_OP_CLASSES -#include "fir/FIROps.h.inc" +#include "optimizer/FIROps.h.inc" LoopOp getForInductionVarOwner(mlir::Value val); @@ -132,4 +132,4 @@ bool isReferenceLike(mlir::Type type); } // namespace fir -#endif // FIR_FIROPS_H +#endif // OPTIMIZER_FIROPS_H diff --git a/include/fir/FIROps.td b/include/optimizer/FIROps.td similarity index 100% rename from include/fir/FIROps.td rename to include/optimizer/FIROps.td diff --git a/include/fir/FIROpsSupport.h b/include/optimizer/FIROpsSupport.h similarity index 95% rename from include/fir/FIROpsSupport.h rename to include/optimizer/FIROpsSupport.h index 450fd20f596e..e7b391480059 100644 --- a/include/fir/FIROpsSupport.h +++ b/include/optimizer/FIROpsSupport.h @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIR_FIROPS_SUPPORT_H -#define FIR_FIROPS_SUPPORT_H +#ifndef OPTIMIZER_FIROPS_SUPPORT_H +#define OPTIMIZER_FIROPS_SUPPORT_H -#include "fir/FIROps.h" #include "mlir/Dialect/StandardOps/Ops.h" +#include "optimizer/FIROps.h" namespace fir { @@ -76,4 +76,4 @@ fir::GlobalOp createGlobalOp(mlir::Location loc, mlir::ModuleOp module, } // namespace fir -#endif // FIR_FIROPS_SUPPORT_H +#endif // OPTIMIZER_FIROPS_SUPPORT_H diff --git a/include/fir/FIRType.h b/include/optimizer/FIRType.h similarity index 99% rename from include/fir/FIRType.h rename to include/optimizer/FIRType.h index a5161c3afa03..13d7c030187b 100644 --- a/include/fir/FIRType.h +++ b/include/optimizer/FIRType.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef DIALECT_FIR_FIRTYPE_H -#define DIALECT_FIR_FIRTYPE_H +#ifndef OPTIMIZER_FIRTYPE_H +#define OPTIMIZER_FIRTYPE_H #include "mlir/IR/Attributes.h" #include "mlir/IR/Types.h" @@ -324,4 +324,4 @@ void printFirType(FIROpsDialect *, mlir::Type ty, mlir::DialectAsmPrinter &p); } // namespace fir -#endif // DIALECT_FIR_FIRTYPE_H +#endif // OPTIMIZER_FIRTYPE_H diff --git a/include/fir/InternalNames.h b/include/optimizer/InternalNames.h similarity index 97% rename from include/fir/InternalNames.h rename to include/optimizer/InternalNames.h index 2fb47a1d5c07..29b8ed912a13 100644 --- a/include/fir/InternalNames.h +++ b/include/optimizer/InternalNames.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIR_INTERNAL_NAMES_H -#define FIR_INTERNAL_NAMES_H +#ifndef OPTIMIZER_INTERNAL_NAMES_H +#define OPTIMIZER_INTERNAL_NAMES_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" @@ -133,4 +133,4 @@ struct NameUniquer { } // namespace fir -#endif // FIR_INTERNAL_NAMES_H +#endif // OPTIMIZER_INTERNAL_NAMES_H diff --git a/include/fir/KindMapping.h b/include/optimizer/KindMapping.h similarity index 96% rename from include/fir/KindMapping.h rename to include/optimizer/KindMapping.h index e747c484a8b9..cca81e64f1be 100644 --- a/include/fir/KindMapping.h +++ b/include/optimizer/KindMapping.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIR_KINDMAPPING_H -#define FIR_KINDMAPPING_H +#ifndef OPTIMIZER_KINDMAPPING_H +#define OPTIMIZER_KINDMAPPING_H #include "llvm/IR/Type.h" #include @@ -88,4 +88,4 @@ class KindMapping { } // namespace fir -#endif // FIR_KINDMAPPING_H +#endif // OPTIMIZER_KINDMAPPING_H diff --git a/include/fir/Transforms/Passes.h b/include/optimizer/Transforms/Passes.h similarity index 92% rename from include/fir/Transforms/Passes.h rename to include/optimizer/Transforms/Passes.h index a33a570464ba..3e09d8711ad9 100644 --- a/include/fir/Transforms/Passes.h +++ b/include/optimizer/Transforms/Passes.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIR_TRANSFORMS_PASSES_H -#define FIR_TRANSFORMS_PASSES_H +#ifndef OPTIMIZER_TRANSFORMS_PASSES_H +#define OPTIMIZER_TRANSFORMS_PASSES_H #include @@ -45,4 +45,4 @@ std::unique_ptr> createMemToRegPass(); } // namespace fir -#endif // FIR_TRANSFORMS_PASSES_H +#endif // OPTIMIZER_TRANSFORMS_PASSES_H diff --git a/include/fir/Transforms/StdConverter.h b/include/optimizer/Transforms/StdConverter.h similarity index 86% rename from include/fir/Transforms/StdConverter.h rename to include/optimizer/Transforms/StdConverter.h index b0ac1fa7aa80..2121a8c50640 100644 --- a/include/fir/Transforms/StdConverter.h +++ b/include/optimizer/Transforms/StdConverter.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef FIR_STD_CONVERTER_H -#define FIR_STD_CONVERTER_H +#ifndef OPTIMIZER_TRANSFORMS_STDCONVERTER_H +#define OPTIMIZER_TRANSFORMS_STDCONVERTER_H #include @@ -30,4 +30,4 @@ std::unique_ptr createFIRToStdPass(KindMapping &); } // namespace fir -#endif // FIR_STD_CONVERSION_H +#endif // OPTIMIZER_TRANSFORMS_STDCONVERTER_H diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index effbf0a84fa2..f610dcaa132b 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -15,7 +15,7 @@ endif() add_subdirectory(common) add_subdirectory(decimal) add_subdirectory(evaluate) -add_subdirectory(fir) add_subdirectory(lower) +add_subdirectory(optimizer) add_subdirectory(parser) add_subdirectory(semantics) diff --git a/lib/fir/.clang-format b/lib/lower/.clang-format similarity index 100% rename from lib/fir/.clang-format rename to lib/lower/.clang-format diff --git a/lib/lower/ast-builder.cc b/lib/lower/ast-builder.cc index 460249cce0eb..3b763d59db89 100644 --- a/lib/lower/ast-builder.cc +++ b/lib/lower/ast-builder.cc @@ -50,8 +50,12 @@ class ASTBuilder { /// Get the result AST::Program *result() { return pgm; } - template constexpr bool Pre(const A &) { return true; } - template constexpr void Post(const A &) {} + template + constexpr bool Pre(const A &) { + return true; + } + template + constexpr void Post(const A &) {} // Module like @@ -257,14 +261,14 @@ class ASTBuilder { return AST::Evaluation{x, s.source, s.label, parents.back()}; }, [&](const auto &x) { - return AST::Evaluation{ - x.value(), s.source, s.label, parents.back()}; + return AST::Evaluation{x.value(), s.source, s.label, + parents.back()}; }, }, s.statement.u); } - AST::Evaluation makeEvalAction( - const Pa::UnlabeledStatement &s) { + AST::Evaluation + makeEvalAction(const Pa::UnlabeledStatement &s) { return std::visit( Co::visitors{ [&](const Pa::ContinueStmt &x) { @@ -280,20 +284,21 @@ class ASTBuilder { s.statement.u); } - template + template AST::Evaluation makeEvalIndirect(const Pa::Statement> &s) { - return AST::Evaluation{ - s.statement.value(), s.source, s.label, parents.back()}; + return AST::Evaluation{s.statement.value(), s.source, s.label, + parents.back()}; } - template + template AST::Evaluation makeEvalDirect(const Pa::Statement &s) { return AST::Evaluation{s.statement, s.source, s.label, parents.back()}; } // When we enter a function-like structure, we want to build a new unit and // set the builder's cursors to point to it. - template bool enterFunc(const A &f) { + template + bool enterFunc(const A &f) { auto &unit = addFunc(AST::FunctionLikeUnit{f, parents.back()}); funclist = &unit.funcs; pushEval(&unit.evals); @@ -309,7 +314,8 @@ class ASTBuilder { // When we enter a construct structure, we want to build a new construct and // set the builder's evaluation cursor to point to it. - template bool enterConstruct(const A &c) { + template + bool enterConstruct(const A &c) { auto &con = addEval(AST::Evaluation{c, parents.back()}); con.subs = new std::list(); pushEval(con.subs); @@ -324,7 +330,8 @@ class ASTBuilder { // When we enter a module structure, we want to build a new module and // set the builder's function cursor to point to it. - template bool enterModule(const A &f) { + template + bool enterModule(const A &f) { auto &unit = addUnit(AST::ModuleLikeUnit{f, parents.back()}); funclist = &unit.funcs; parents.emplace_back(&unit); @@ -336,12 +343,14 @@ class ASTBuilder { parents.pop_back(); } - template A &addUnit(const A &unit) { + template + A &addUnit(const A &unit) { pgm->getUnits().emplace_back(unit); return std::get(pgm->getUnits().back()); } - template A &addFunc(const A &func) { + template + A &addFunc(const A &func) { if (funclist) { funclist->emplace_back(func); return funclist->back(); @@ -376,9 +385,10 @@ class ASTBuilder { std::vector parents; }; -template constexpr bool hasErrLabel(const A &stmt) { +template +constexpr bool hasErrLabel(const A &stmt) { if constexpr (std::is_same_v || - std::is_same_v) { + std::is_same_v) { for (const auto &control : stmt.controls) { if (std::holds_alternative(control.u)) { return true; @@ -386,10 +396,12 @@ template constexpr bool hasErrLabel(const A &stmt) { } } if constexpr (std::is_same_v || - std::is_same_v || std::is_same_v || - std::is_same_v || - std::is_same_v || std::is_same_v || - std::is_same_v) { + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) { for (const auto &spec : stmt.v) { if (std::holds_alternative(spec.u)) { return true; @@ -406,9 +418,10 @@ template constexpr bool hasErrLabel(const A &stmt) { return false; } -template constexpr bool hasEorLabel(const A &stmt) { +template +constexpr bool hasEorLabel(const A &stmt) { if constexpr (std::is_same_v || - std::is_same_v) { + std::is_same_v) { for (const auto &control : stmt.controls) { if (std::holds_alternative(control.u)) { return true; @@ -425,9 +438,10 @@ template constexpr bool hasEorLabel(const A &stmt) { return false; } -template constexpr bool hasEndLabel(const A &stmt) { +template +constexpr bool hasEndLabel(const A &stmt) { if constexpr (std::is_same_v || - std::is_same_v) { + std::is_same_v) { for (const auto &control : stmt.controls) { if (std::holds_alternative(control.u)) { return true; @@ -457,22 +471,22 @@ bool hasAltReturns(const Pa::CallStmt &callStmt) { /// Determine if `callStmt` has alternate returns and if so set `e` to be the /// origin of a switch-like control flow -void altRet( - AST::Evaluation &e, const Pa::CallStmt *callStmt, AST::Evaluation *cstr) { +void altRet(AST::Evaluation &e, const Pa::CallStmt *callStmt, + AST::Evaluation *cstr) { if (hasAltReturns(*callStmt)) { e.setCFG(AST::CFGAnnotation::Switch, cstr); } } -template +template void ioLabel(AST::Evaluation &e, const A *s, AST::Evaluation *cstr) { if (hasErrLabel(*s) || hasEorLabel(*s) || hasEndLabel(*s)) { e.setCFG(AST::CFGAnnotation::IoSwitch, cstr); } } -void annotateEvalListCFG( - std::list &evals, AST::Evaluation *cstr) { +void annotateEvalListCFG(std::list &evals, + AST::Evaluation *cstr) { bool nextIsTarget = false; for (auto &e : evals) { e.isTarget = nextIsTarget; @@ -703,8 +717,8 @@ L::StringRef evalName(AST::Evaluation &e) { e.u); } -void dumpEvalList( - L::raw_ostream &o, std::list &evals, int indent = 1) { +void dumpEvalList(L::raw_ostream &o, std::list &evals, + int indent = 1) { static const std::string white{" ++"}; std::string indentString{white.substr(0, indent * 2)}; for (AST::Evaluation &e : evals) { @@ -753,7 +767,7 @@ void dumpFunctionLikeUnit(L::raw_ostream &o, AST::FunctionLikeUnit &flu) { } }, }, - flu.funStmts.front()); + flu.funStmts.front()); o << unitKind << ' ' << name; if (header.size()) { o << ": " << header; @@ -763,11 +777,11 @@ void dumpFunctionLikeUnit(L::raw_ostream &o, AST::FunctionLikeUnit &flu) { o << "End" << unitKind << ' ' << name << "\n\n"; } -} // namespace +} // namespace -Br::AST::FunctionLikeUnit::FunctionLikeUnit( - const Pa::MainProgram &f, const AST::ParentType &parent) - : ProgramUnit{&f, parent} { +Br::AST::FunctionLikeUnit::FunctionLikeUnit(const Pa::MainProgram &f, + const AST::ParentType &parent) + : ProgramUnit{&f, parent} { auto &ps{std::get>>(f.t)}; if (ps.has_value()) { const Pa::Statement &s{ps.value()}; @@ -776,44 +790,44 @@ Br::AST::FunctionLikeUnit::FunctionLikeUnit( funStmts.push_back(&std::get>(f.t)); } -Br::AST::FunctionLikeUnit::FunctionLikeUnit( - const Pa::FunctionSubprogram &f, const AST::ParentType &parent) - : ProgramUnit{&f, parent} { +Br::AST::FunctionLikeUnit::FunctionLikeUnit(const Pa::FunctionSubprogram &f, + const AST::ParentType &parent) + : ProgramUnit{&f, parent} { funStmts.push_back(&std::get>(f.t)); funStmts.push_back(&std::get>(f.t)); } -Br::AST::FunctionLikeUnit::FunctionLikeUnit( - const Pa::SubroutineSubprogram &f, const AST::ParentType &parent) - : ProgramUnit{&f, parent} { +Br::AST::FunctionLikeUnit::FunctionLikeUnit(const Pa::SubroutineSubprogram &f, + const AST::ParentType &parent) + : ProgramUnit{&f, parent} { funStmts.push_back(&std::get>(f.t)); funStmts.push_back(&std::get>(f.t)); } Br::AST::FunctionLikeUnit::FunctionLikeUnit( const Pa::SeparateModuleSubprogram &f, const AST::ParentType &parent) - : ProgramUnit{&f, parent} { + : ProgramUnit{&f, parent} { funStmts.push_back(&std::get>(f.t)); funStmts.push_back(&std::get>(f.t)); } -Br::AST::ModuleLikeUnit::ModuleLikeUnit( - const Pa::Module &m, const AST::ParentType &parent) - : ProgramUnit{&m, parent} { +Br::AST::ModuleLikeUnit::ModuleLikeUnit(const Pa::Module &m, + const AST::ParentType &parent) + : ProgramUnit{&m, parent} { modStmts.push_back(&std::get>(m.t)); modStmts.push_back(&std::get>(m.t)); } -Br::AST::ModuleLikeUnit::ModuleLikeUnit( - const Pa::Submodule &m, const AST::ParentType &parent) - : ProgramUnit{&m, parent} { +Br::AST::ModuleLikeUnit::ModuleLikeUnit(const Pa::Submodule &m, + const AST::ParentType &parent) + : ProgramUnit{&m, parent} { modStmts.push_back(&std::get>(m.t)); modStmts.push_back(&std::get>(m.t)); } -Br::AST::BlockDataUnit::BlockDataUnit( - const Pa::BlockData &db, const AST::ParentType &parent) - : ProgramUnit{&db, parent} {} +Br::AST::BlockDataUnit::BlockDataUnit(const Pa::BlockData &db, + const AST::ParentType &parent) + : ProgramUnit{&db, parent} {} AST::Program *Br::createAST(const Pa::Program &root) { ASTBuilder walker; @@ -837,7 +851,7 @@ void Br::annotateControl(AST::Program &ast) { } }, }, - unit); + unit); } } diff --git a/lib/lower/ast-builder.h b/lib/lower/ast-builder.h index fbb70830e613..4e232e6257e4 100644 --- a/lib/lower/ast-builder.h +++ b/lib/lower/ast-builder.h @@ -53,21 +53,22 @@ struct CGJump { }; /// is `A` a construct (or directive)? -template constexpr static bool isConstruct() { +template +constexpr static bool isConstruct() { return std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v; + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v; } /// Function-like units can contains lists of evaluations. These can be @@ -130,37 +131,40 @@ struct Evaluation { Evaluation(const Evaluation &) = default; /// General ctor - template + template Evaluation(const A &a, const parser::CharBlock &pos, - const std::optional &lab, const ParentType &p) - : u{&a}, parent{p}, pos{pos}, lab{lab} {} + const std::optional &lab, const ParentType &p) + : u{&a}, parent{p}, pos{pos}, lab{lab} {} /// Compiler-generated jump Evaluation(const CGJump &jump, const ParentType &p) - : u{jump}, parent{p}, cfg{CFGAnnotation::Goto} {} + : u{jump}, parent{p}, cfg{CFGAnnotation::Goto} {} /// Construct ctor - template + template Evaluation(const A &a, const ParentType &parent) : u{&a}, parent{parent} { static_assert(AST::isConstruct(), "must be a construct"); } /// is `A` executable (an action statement or compiler generated)? - template constexpr static bool isAction(const A &a) { + template + constexpr static bool isAction(const A &a) { return !AST::isConstruct() && !isOther(a); } /// is `A` a compiler-generated evaluation? - template constexpr static bool isGenerated(const A &) { + template + constexpr static bool isGenerated(const A &) { return std::is_same_v; } /// is `A` not an executable statement? - template constexpr static bool isOther(const A &) { + template + constexpr static bool isOther(const A &) { return std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v; + std::is_same_v || + std::is_same_v || + std::is_same_v; } constexpr bool isActionStmt() const { @@ -168,7 +172,7 @@ struct Evaluation { [](auto *p) { return isAction(*p); }, [](auto &r) { return isGenerated(r); }, }, - u); + u); } constexpr bool isStmt() const { @@ -176,7 +180,7 @@ struct Evaluation { [](auto *p) { return isAction(*p) || isOther(*p); }, [](auto &r) { return isGenerated(r); }, }, - u); + u); } constexpr bool isConstruct() const { return !isStmt(); } @@ -211,22 +215,23 @@ struct Evaluation { ParentType parent; parser::CharBlock pos; std::optional lab; - std::list *subs{nullptr}; // construct sub-statements + std::list *subs{nullptr}; // construct sub-statements CFGAnnotation cfg{CFGAnnotation::None}; - bool isTarget{false}; // this evaluation is a control target - bool containsBranches{false}; // construct contains branches + bool isTarget{false}; // this evaluation is a control target + bool containsBranches{false}; // construct contains branches }; /// A program is a list of program units. /// These units can be function like, module like, or block data struct ProgramUnit { - template + template ProgramUnit(A *ptr, const ParentType &parent) : p{ptr}, parent{parent} {} std::variant + const parser::SubroutineSubprogram *, const parser::Module *, + const parser::Submodule *, + const parser::SeparateModuleSubprogram *, + const parser::BlockData *> p; ParentType parent; }; @@ -237,21 +242,21 @@ struct FunctionLikeUnit : public ProgramUnit { // wrapper statements for function-like syntactic structures using FunctionStatement = std::variant *, - const parser::Statement *, - const parser::Statement *, - const parser::Statement *, - const parser::Statement *, - const parser::Statement *, - const parser::Statement *, - const parser::Statement *>; + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *, + const parser::Statement *>; FunctionLikeUnit(const parser::MainProgram &f, const ParentType &parent); - FunctionLikeUnit( - const parser::FunctionSubprogram &f, const ParentType &parent); - FunctionLikeUnit( - const parser::SubroutineSubprogram &f, const ParentType &parent); - FunctionLikeUnit( - const parser::SeparateModuleSubprogram &f, const ParentType &parent); + FunctionLikeUnit(const parser::FunctionSubprogram &f, + const ParentType &parent); + FunctionLikeUnit(const parser::SubroutineSubprogram &f, + const ParentType &parent); + FunctionLikeUnit(const parser::SeparateModuleSubprogram &f, + const ParentType &parent); bool isMainProgram() { return std::get_if *>( @@ -267,13 +272,14 @@ struct FunctionLikeUnit : public ProgramUnit { return isA(); } - const semantics::Scope *scope{nullptr}; // scope from front-end + const semantics::Scope *scope{nullptr}; // scope from front-end std::list funStmts; // begin/end pair - std::list evals; // statements - std::list funcs; // internal procedures + std::list evals; // statements + std::list funcs; // internal procedures private: - template const A *isA() { + template + const A *isA() { if (auto p = std::get_if *>(&funStmts.front())) { return &(*p)->statement; } @@ -287,9 +293,9 @@ struct ModuleLikeUnit : public ProgramUnit { // wrapper statements for module-like syntactic structures using ModuleStatement = std::variant *, - const parser::Statement *, - const parser::Statement *, - const parser::Statement *>; + const parser::Statement *, + const parser::Statement *, + const parser::Statement *>; ModuleLikeUnit(const parser::Module &m, const ParentType &parent); ModuleLikeUnit(const parser::Submodule &m, const ParentType &parent); @@ -314,7 +320,7 @@ struct Program { std::list units; }; -} // namespace AST +} // namespace AST /// Create an AST from the parse tree AST::Program *createAST(const parser::Program &root); @@ -327,6 +333,6 @@ void annotateControl(AST::Program &ast); void dumpAST(llvm::raw_ostream &o, AST::Program &ast); -} // namespace lower +} // namespace Fortran::lower -#endif // FORTRAN_LOWER_AST_BUILDER_H_ +#endif // FORTRAN_LOWER_AST_BUILDER_H_ diff --git a/lib/lower/bridge.cc b/lib/lower/bridge.cc index 468201c0d1d0..fbdbde349be8 100644 --- a/lib/lower/bridge.cc +++ b/lib/lower/bridge.cc @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "bridge.h" +#include "../parser/parse-tree.h" +#include "../semantics/tools.h" #include "ast-builder.h" #include "builder.h" #include "convert-expr.h" @@ -14,19 +16,17 @@ #include "intrinsics.h" #include "io.h" #include "mangler.h" -#include "runtime.h" -#include "../parser/parse-tree.h" -#include "../semantics/tools.h" -#include "fir/FIRDialect.h" -#include "fir/FIROps.h" -#include "fir/FIRType.h" -#include "fir/InternalNames.h" -#include "llvm/Support/CommandLine.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/StandardOps/Ops.h" #include "mlir/IR/Builders.h" #include "mlir/Parser.h" #include "mlir/Target/LLVMIR.h" +#include "optimizer/FIRDialect.h" +#include "optimizer/FIROps.h" +#include "optimizer/FIRType.h" +#include "optimizer/InternalNames.h" +#include "runtime.h" +#include "llvm/Support/CommandLine.h" namespace Br = Fortran::lower; namespace Co = Fortran::common; @@ -42,11 +42,12 @@ using namespace Fortran::lower; namespace { L::cl::opt ClDumpPreFir("fdebug-dump-pre-fir", L::cl::init(false), - L::cl::desc("dump the IR tree prior to FIR")); + L::cl::desc("dump the IR tree prior to FIR")); -L::cl::opt ClDisableToDoAssert("disable-burnside-todo", - L::cl::desc("disable burnside bridge asserts"), L::cl::init(false), - L::cl::Hidden); +L::cl::opt + ClDisableToDoAssert("disable-burnside-todo", + L::cl::desc("disable burnside bridge asserts"), + L::cl::init(false), L::cl::Hidden); #undef TODO #define TODO() assert(false && "not implemented yet") @@ -66,13 +67,13 @@ constexpr static bool isStopStmt(const Pa::StopStmt &stm) { #include "cfg-builder.h" #undef TODO -#define TODO() \ - { \ - if (ClDisableToDoAssert) \ - M::emitError(toLocation(), __FILE__) \ - << ":" << __LINE__ << " not implemented"; \ - else \ - assert(false && "not yet implemented"); \ +#define TODO() \ + { \ + if (ClDisableToDoAssert) \ + M::emitError(toLocation(), __FILE__) \ + << ":" << __LINE__ << " not implemented"; \ + else \ + assert(false && "not yet implemented"); \ } /// Converter from AST to FIR @@ -95,8 +96,8 @@ class FirConverter : public AbstractConverter { return createSomeExpression(loc, *this, *expr, localSymbols, intrinsics); } M::Value createLogicalExprAsI1(M::Location loc, const Se::SomeExpr *expr) { - return createI1LogicalExpression( - loc, *this, *expr, localSymbols, intrinsics); + return createI1LogicalExpression(loc, *this, *expr, localSymbols, + intrinsics); } M::Value createTemporary(M::Location loc, const Se::Symbol &sym) { return Br::createTemporary(loc, *builder, localSymbols, genType(sym), &sym); @@ -104,8 +105,8 @@ class FirConverter : public AbstractConverter { // TODO: we need a map for the various Fortran runtime entry points M::FuncOp genRuntimeFunction(RuntimeEntryCode rec, int kind) { - return genFunctionFIR( - getRuntimeEntryName(rec), getRuntimeEntryType(rec, mlirContext, kind)); + return genFunctionFIR(getRuntimeEntryName(rec), + getRuntimeEntryType(rec, mlirContext, kind)); } M::FuncOp genFunctionFIR(L::StringRef callee, M::FunctionType funcTy) { @@ -152,39 +153,39 @@ class FirConverter : public AbstractConverter { cstr->parent); } - template + template static const Se::SomeExpr *getScalarExprOfTuple(const A &tuple) { return Se::GetExpr(std::get(tuple)); } - template + template static const Se::SomeExpr *getExprOfTuple(const A &tuple) { return Se::GetExpr(std::get(tuple)); } /// Get the condition expression for a CondGoto evaluation const Se::SomeExpr *getEvaluationCondition(AST::Evaluation &eval) { - return std::visit( - Co::visitors{ - [&](const Pa::IfStmt *stmt) { - return getScalarExprOfTuple(stmt->t); - }, - [&](const Pa::IfThenStmt *stmt) { - return getScalarExprOfTuple(stmt->t); - }, - [&](const Pa::ElseIfStmt *stmt) { - return getScalarExprOfTuple(stmt->t); - }, - [&](const Pa::WhereConstructStmt *stmt) { - return getExprOfTuple(stmt->t); - }, - [&](const Pa::MaskedElsewhereStmt *stmt) { - return getExprOfTuple(stmt->t); - }, - [&](auto) -> const Se::SomeExpr * { - M::emitError(toLocation(), "unexpected conditional branch case"); - return nullptr; - }, - }, - eval.u); + return std::visit(Co::visitors{ + [&](const Pa::IfStmt *stmt) { + return getScalarExprOfTuple(stmt->t); + }, + [&](const Pa::IfThenStmt *stmt) { + return getScalarExprOfTuple(stmt->t); + }, + [&](const Pa::ElseIfStmt *stmt) { + return getScalarExprOfTuple(stmt->t); + }, + [&](const Pa::WhereConstructStmt *stmt) { + return getExprOfTuple(stmt->t); + }, + [&](const Pa::MaskedElsewhereStmt *stmt) { + return getExprOfTuple(stmt->t); + }, + [&](auto) -> const Se::SomeExpr * { + M::emitError(toLocation(), + "unexpected conditional branch case"); + return nullptr; + }, + }, + eval.u); } // @@ -192,17 +193,17 @@ class FirConverter : public AbstractConverter { // void genFIR(const Pa::Statement &stmt, std::string &name, - const Se::Symbol *&) { + const Se::Symbol *&) { setCurrentPosition(stmt.source); name = uniquer.doProgramEntry(); } void genFIR(const Pa::Statement &stmt, std::string &, - const Se::Symbol *&) { + const Se::Symbol *&) { setCurrentPosition(stmt.source); genFIR(stmt.statement); } void genFIR(const Pa::Statement &stmt, std::string &name, - const Se::Symbol *&symbol) { + const Se::Symbol *&symbol) { setCurrentPosition(stmt.source); auto &n{std::get(stmt.statement.t)}; symbol = n.symbol; @@ -210,13 +211,13 @@ class FirConverter : public AbstractConverter { name = mangleName(*symbol); } void genFIR(const Pa::Statement &stmt, std::string &, - const Se::Symbol *&symbol) { + const Se::Symbol *&symbol) { setCurrentPosition(stmt.source); assert(symbol); genFIRFunctionReturn(*symbol); } void genFIR(const Pa::Statement &stmt, std::string &name, - const Se::Symbol *&symbol) { + const Se::Symbol *&symbol) { setCurrentPosition(stmt.source); auto &n{std::get(stmt.statement.t)}; symbol = n.symbol; @@ -224,19 +225,19 @@ class FirConverter : public AbstractConverter { name = mangleName(*symbol); } void genFIR(const Pa::Statement &stmt, std::string &, - const Se::Symbol *&) { + const Se::Symbol *&) { setCurrentPosition(stmt.source); genFIR(stmt.statement); } void genFIR(const Pa::Statement &stmt, - std::string &name, const Se::Symbol *&symbol) { + std::string &name, const Se::Symbol *&symbol) { setCurrentPosition(stmt.source); auto &n{stmt.statement.v}; name = n.ToString(); symbol = n.symbol; } void genFIR(const Pa::Statement &stmt, std::string &, - const Se::Symbol *&) { + const Se::Symbol *&) { setCurrentPosition(stmt.source); genFIR(stmt.statement); } @@ -260,7 +261,8 @@ class FirConverter : public AbstractConverter { M::Value r{builder->create(toLocation(), resultRef)}; builder->create(toLocation(), r); } - template void genFIRProcedureExit(const A *) { + template + void genFIRProcedureExit(const A *) { // FIXME: alt-returns builder->create(toLocation()); } @@ -285,20 +287,20 @@ class FirConverter : public AbstractConverter { genFIRCondBranch(cond, targets[0], targets[1]); } - void genFIRCondBranch( - M::Value cond, AST::Evaluation *trueDest, AST::Evaluation *falseDest) { + void genFIRCondBranch(M::Value cond, AST::Evaluation *trueDest, + AST::Evaluation *falseDest) { using namespace std::placeholders; localEdgeQ.emplace_back(std::bind( [](M::OpBuilder *builder, M::Block *block, M::Value cnd, - AST::Evaluation *trueDest, AST::Evaluation *falseDest, - M::Location location, const LabelMapType &map) { + AST::Evaluation *trueDest, AST::Evaluation *falseDest, + M::Location location, const LabelMapType &map) { L::SmallVector blk; builder->setInsertionPointToEnd(block); auto tdp{map.find(trueDest)}; auto fdp{map.find(falseDest)}; assert(tdp != map.end() && fdp != map.end()); - builder->create( - location, cnd, tdp->second, blk, fdp->second, blk); + builder->create(location, cnd, tdp->second, blk, + fdp->second, blk); }, builder, builder->getInsertionBlock(), cond, trueDest, falseDest, toLocation(), _1)); @@ -311,7 +313,7 @@ class FirConverter : public AbstractConverter { using namespace std::placeholders; localEdgeQ.emplace_back(std::bind( [](M::OpBuilder *builder, M::Block *block, AST::Evaluation *dest, - M::Location location, const LabelMapType &map) { + M::Location location, const LabelMapType &map) { builder->setInsertionPointToEnd(block); assert(map.find(dest) != map.end() && "no destination"); builder->create(location, map.find(dest)->second); @@ -346,7 +348,7 @@ class FirConverter : public AbstractConverter { void switchInsertionPointToOtherwise(fir::WhereOp &where) { builder->setInsertionPointToStart(&where.otherRegion().front()); } - template + template void genWhereCondition(fir::WhereOp &where, const A *stmt) { auto cond{createLogicalExprAsI1( toLocation(), Se::GetExpr(std::get(stmt->t)))}; @@ -356,7 +358,8 @@ class FirConverter : public AbstractConverter { M::Value genFIRLoopIndex(const Pa::ScalarExpr &x) { return builder->create(toLocation(), - M::IndexType::get(&mlirContext), genExprValue(*Se::GetExpr(x))); + M::IndexType::get(&mlirContext), + genExprValue(*Se::GetExpr(x))); } /// Structured control op (`fir.loop`, `fir.where`) @@ -387,8 +390,8 @@ class FirConverter : public AbstractConverter { if (x.step.has_value()) { step.emplace_back(genExprValue(*Se::GetExpr(*x.step))); } - doLoop = builder->create( - toLocation(), lo, hi, step); + doLoop = builder->create(toLocation(), lo, hi, + step); builder->setInsertionPointToStart(doLoop.getBody()); auto *sym{x.name.thing.symbol}; auto ty{genType(*sym)}; @@ -403,8 +406,9 @@ class FirConverter : public AbstractConverter { }, [&](const Pa::LoopControl::Concurrent &x) { // FIXME: can project a multi-dimensional space - doLoop = builder->create(toLocation(), - M::Value{}, M::Value{}, L::ArrayRef{}); + doLoop = builder->create( + toLocation(), M::Value{}, M::Value{}, + L::ArrayRef{}); builder->setInsertionPointToStart(doLoop.getBody()); }, }, @@ -498,7 +502,7 @@ class FirConverter : public AbstractConverter { }, [&](const Pa::ProcComponentRef &) { TODO(); }, }, - std::get(stmt.v.t).u); + std::get(stmt.v.t).u); for (auto *d : argsList) { Se::SymbolRef sr{*d}; // FIXME: @@ -507,7 +511,7 @@ class FirConverter : public AbstractConverter { auto funTy{M::FunctionType::get(argTy, resTy, builder->getContext())}; // FIXME: mangle name M::FuncOp func{getFunc(funName, funTy)}; - (void)func; // FIXME + (void)func; // FIXME std::vector actuals; for (auto &aa : std::get>(stmt.v.t)) { auto &kw = std::get>(aa.t); @@ -522,7 +526,7 @@ class FirConverter : public AbstractConverter { [&](const Pa::ActualArg::PercentRef &) { TODO(); }, [&](const Pa::ActualArg::PercentVal &) { TODO(); }, }, - arg.u); + arg.u); if (kw.has_value()) { TODO(); continue; @@ -530,8 +534,8 @@ class FirConverter : public AbstractConverter { actuals.push_back(fe); } - builder->create( - toLocation(), resTy, builder->getSymbolRefAttr(funName), actuals); + builder->create(toLocation(), resTy, + builder->getSymbolRefAttr(funName), actuals); } void genFIR(const Pa::IfStmt &) { TODO(); } @@ -605,7 +609,7 @@ class FirConverter : public AbstractConverter { genFIR(b.statement); }, }, - s.u); + s.u); } TODO(); } @@ -630,13 +634,13 @@ class FirConverter : public AbstractConverter { void genFIR(const Pa::EndCriticalStmt &) { TODO(); } // Do loop is handled by EvalIterative(), EvalStructuredOp() - void genFIR(const Pa::NonLabelDoStmt &) {} // do nothing - void genFIR(const Pa::EndDoStmt &) {} // do nothing + void genFIR(const Pa::NonLabelDoStmt &) {} // do nothing + void genFIR(const Pa::EndDoStmt &) {} // do nothing // If-Then-Else is handled by EvalCondGoto(), EvalStructuredOp() - void genFIR(const Pa::IfThenStmt &) {} // do nothing - void genFIR(const Pa::ElseIfStmt &) {} // do nothing - void genFIR(const Pa::ElseStmt &) {} // do nothing + void genFIR(const Pa::IfThenStmt &) {} // do nothing + void genFIR(const Pa::ElseIfStmt &) {} // do nothing + void genFIR(const Pa::ElseStmt &) {} // do nothing void genFIR(const Pa::EndIfStmt &) {} // do nothing void genFIR(const Pa::SelectRankStmt &) { TODO(); } @@ -727,7 +731,8 @@ class FirConverter : public AbstractConverter { step.emplace_back(one); // Copy the minimum of the lhs and rhs lengths auto cmpLen{builder->create(toLocation(), M::CmpIPredicate::slt, - lhsAddrAndLen.second, rhsAddrAndLen.second)}; + lhsAddrAndLen.second, + rhsAddrAndLen.second)}; auto copyLen{builder->create( toLocation(), cmpLen, lhsAddrAndLen.second, rhsAddrAndLen.second)}; auto copyMaxIndex{ @@ -746,15 +751,15 @@ class FirConverter : public AbstractConverter { builder->setInsertionPointToEnd(insPt); // Build loop to pad lhs with blanks if needed. - auto padMaxIndex{builder->create( - toLocation(), indexTy, lhsAddrAndLen.second)}; + auto padMaxIndex{builder->create(toLocation(), indexTy, + lhsAddrAndLen.second)}; auto byteTy{M::IntegerType::get(8, builder->getContext())}; auto asciiSpace{builder->create( toLocation(), byteTy, builder->getIntegerAttr(byteTy, 32))}; auto blank{ builder->create(toLocation(), charType, asciiSpace)}; - auto padLoop{builder->create( - toLocation(), copyMaxIndex, padMaxIndex, step)}; + auto padLoop{builder->create(toLocation(), copyMaxIndex, + padMaxIndex, step)}; insPt = builder->getInsertionBlock(); builder->setInsertionPointToStart(padLoop.getBody()); auto padIndex{padLoop.getInductionVar()}; @@ -795,7 +800,8 @@ class FirConverter : public AbstractConverter { // Conversions are already inserted by semantic // analysis. builder->create(toLocation(), - genExprValue(assignment->rhs), genExprAddr(assignment->lhs)); + genExprValue(assignment->rhs), + genExprAddr(assignment->lhs)); break; case CharacterCat: // Fortran 2018 10.2.1.3 p10 and p11 @@ -813,7 +819,7 @@ class FirConverter : public AbstractConverter { } } - void genFIR(const Pa::ContinueStmt &) {} // do nothing + void genFIR(const Pa::ContinueStmt &) {} // do nothing void genFIR(const Pa::DeallocateStmt &) { TODO(); } void genFIR(const Pa::EventPostStmt &) { // call some runtime routine @@ -836,20 +842,21 @@ class FirConverter : public AbstractConverter { /// We do this by setting each pointer to null. void genFIR(const Pa::NullifyStmt &stmt) { for (auto &po : stmt.v) { - std::visit(Co::visitors{ - [&](const Pa::Name &sym) { - auto ty{genType(*sym.symbol)}; - auto load{builder->create(toLocation(), - localSymbols.lookupSymbol(*sym.symbol))}; - auto idxTy{M::IndexType::get(&mlirContext)}; - auto zero{builder->create(toLocation(), - idxTy, builder->getIntegerAttr(idxTy, 0))}; - auto cast{builder->create( - toLocation(), ty, zero)}; - builder->create(toLocation(), cast, load); - }, - [&](const Pa::StructureComponent &) { TODO(); }, - }, + std::visit( + Co::visitors{ + [&](const Pa::Name &sym) { + auto ty{genType(*sym.symbol)}; + auto load{builder->create( + toLocation(), localSymbols.lookupSymbol(*sym.symbol))}; + auto idxTy{M::IndexType::get(&mlirContext)}; + auto zero{builder->create( + toLocation(), idxTy, builder->getIntegerAttr(idxTy, 0))}; + auto cast{ + builder->create(toLocation(), ty, zero)}; + builder->create(toLocation(), cast, load); + }, + [&](const Pa::StructureComponent &) { TODO(); }, + }, po.u); } } @@ -889,7 +896,7 @@ class FirConverter : public AbstractConverter { // call FAIL IMAGE in runtime void genFIR(const Pa::FailImageStmt &stmt) { auto callee{genRuntimeFunction(FIRT_FAIL_IMAGE, 0)}; - L::SmallVector operands; // FAIL IMAGE has no args + L::SmallVector operands; // FAIL IMAGE has no args builder->create(toLocation(), callee, operands); } @@ -897,7 +904,7 @@ class FirConverter : public AbstractConverter { void genFIR(const Pa::StopStmt &stm) { auto callee{ genRuntimeFunction(isStopStmt(stm) ? FIRT_STOP : FIRT_ERROR_STOP, - defaults.GetDefaultKind(IntegerCat))}; + defaults.GetDefaultKind(IntegerCat))}; // 2 args: stop-code-opt, quiet-opt L::SmallVector operands; builder->create(toLocation(), callee, operands); @@ -921,7 +928,7 @@ class FirConverter : public AbstractConverter { } // stubs for generic goto statements; see genFIREvalGoto() - void genFIR(const Pa::CycleStmt &) {} // do nothing + void genFIR(const Pa::CycleStmt &) {} // do nothing void genFIR(const Pa::ExitStmt &) {} // do nothing void genFIR(const Pa::GotoStmt &) {} // do nothing @@ -931,7 +938,7 @@ class FirConverter : public AbstractConverter { [&](const auto *p) { genFIR(*p); }, [](const AST::CGJump &) { /* do nothing */ }, }, - eval.u); + eval.u); } /// Lower an Evaluation @@ -945,18 +952,36 @@ class FirConverter : public AbstractConverter { // start a new block } switch (eval.cfg) { - case AST::CFGAnnotation::None: genFIREvalNone(eval); break; - case AST::CFGAnnotation::Goto: genFIREvalGoto(eval); break; - case AST::CFGAnnotation::CondGoto: genFIREvalCondGoto(eval); break; - case AST::CFGAnnotation::IndGoto: genFIREvalIndGoto(eval); break; - case AST::CFGAnnotation::IoSwitch: genFIREvalIoSwitch(eval); break; - case AST::CFGAnnotation::Switch: genFIREvalSwitch(eval); break; - case AST::CFGAnnotation::Iterative: genFIREvalIterative(eval); break; + case AST::CFGAnnotation::None: + genFIREvalNone(eval); + break; + case AST::CFGAnnotation::Goto: + genFIREvalGoto(eval); + break; + case AST::CFGAnnotation::CondGoto: + genFIREvalCondGoto(eval); + break; + case AST::CFGAnnotation::IndGoto: + genFIREvalIndGoto(eval); + break; + case AST::CFGAnnotation::IoSwitch: + genFIREvalIoSwitch(eval); + break; + case AST::CFGAnnotation::Switch: + genFIREvalSwitch(eval); + break; + case AST::CFGAnnotation::Iterative: + genFIREvalIterative(eval); + break; case AST::CFGAnnotation::FirStructuredOp: genFIREvalStructuredOp(eval); break; - case AST::CFGAnnotation::Return: genFIREvalReturn(eval); break; - case AST::CFGAnnotation::Terminate: genFIREvalTerminate(eval); break; + case AST::CFGAnnotation::Return: + genFIREvalReturn(eval); + break; + case AST::CFGAnnotation::Terminate: + genFIREvalTerminate(eval); + break; } } @@ -971,7 +996,7 @@ class FirConverter : public AbstractConverter { /// Prepare to translate a new function void startNewFunction(AST::FunctionLikeUnit &funit, L::StringRef name, - const Se::Symbol *symbol) { + const Se::Symbol *symbol) { M::FuncOp func{getNamedFunction(module, name)}; if (!func) { func = createNewFunction(name, symbol); @@ -988,11 +1013,11 @@ class FirConverter : public AbstractConverter { auto *details{symbol->detailsIf()}; assert(details && "details for semantics::Symbol must be subprogram"); for (const auto &v : - L::zip(details->dummyArgs(), entryBlock->getArguments())) { + L::zip(details->dummyArgs(), entryBlock->getArguments())) { if (std::get<0>(v)) { localSymbols.addSymbol(*std::get<0>(v), std::get<1>(v)); } else { - TODO(); // handle alternate return + TODO(); // handle alternate return } } if (details->isFunction()) { @@ -1019,7 +1044,7 @@ class FirConverter : public AbstractConverter { /// Lower a procedure-like construct void lowerFunc(AST::FunctionLikeUnit &func, L::ArrayRef modules, - L::Optional host = {}) { + L::Optional host = {}) { std::string name; const Se::Symbol *symbol{nullptr}; auto size{func.funStmts.size()}; @@ -1027,8 +1052,8 @@ class FirConverter : public AbstractConverter { assert((size == 1 || size == 2) && "ill-formed subprogram"); if (size == 2) { currentEvaluation = nullptr; - std::visit( - [&](auto *p) { genFIR(*p, name, symbol); }, func.funStmts.front()); + std::visit([&](auto *p) { genFIR(*p, name, symbol); }, + func.funStmts.front()); } else { name = uniquer.doProgramEntry(); } @@ -1040,8 +1065,8 @@ class FirConverter : public AbstractConverter { lowerEval(e); } currentEvaluation = nullptr; - std::visit( - [&](auto *p) { genFIR(*p, name, symbol); }, func.funStmts.back()); + std::visit([&](auto *p) { genFIR(*p, name, symbol); }, + func.funStmts.back()); endNewFunction(); @@ -1115,7 +1140,7 @@ class FirConverter : public AbstractConverter { M::Location toLocation() { return toLocation(currentPosition); } // TODO: should these be moved to convert-expr? - template + template M::Value genCompare(M::Value lhs, M::Value rhs) { auto lty{lhs->getType()}; assert(lty == rhs->getType()); @@ -1159,7 +1184,7 @@ class FirConverter : public AbstractConverter { Pa::CharBlock currentPosition; CFGMapType cfgMap; std::list cfgEdgeSetPool; - AST::Evaluation *currentEvaluation{nullptr}; // FIXME: this is a hack + AST::Evaluation *currentEvaluation{nullptr}; // FIXME: this is a hack public: FirConverter() = delete; @@ -1168,11 +1193,11 @@ class FirConverter : public AbstractConverter { virtual ~FirConverter() = default; explicit FirConverter(BurnsideBridge &bridge, fir::NameUniquer &uniquer) - : mlirContext{bridge.getMLIRContext()}, cooked{bridge.getCookedSource()}, - module{bridge.getModule()}, defaults{bridge.getDefaultKinds()}, - intrinsics{IntrinsicLibrary::create( - IntrinsicLibrary::Version::LLVM, bridge.getMLIRContext())}, - uniquer{uniquer} {} + : mlirContext{bridge.getMLIRContext()}, cooked{bridge.getCookedSource()}, + module{bridge.getModule()}, defaults{bridge.getDefaultKinds()}, + intrinsics{IntrinsicLibrary::create(IntrinsicLibrary::Version::LLVM, + bridge.getMLIRContext())}, + uniquer{uniquer} {} /// Convert the AST to FIR void run(AST::Program &ast) { @@ -1183,7 +1208,7 @@ class FirConverter : public AbstractConverter { [&](AST::ModuleLikeUnit &m) { pruneMod(m); }, [](AST::BlockDataUnit &) { /* do nothing */ }, }, - u); + u); } // do translation @@ -1193,7 +1218,7 @@ class FirConverter : public AbstractConverter { [&](AST::ModuleLikeUnit &m) { lowerMod(m); }, [&](AST::BlockDataUnit &) { TODO(); }, }, - u); + u); } } @@ -1204,12 +1229,12 @@ class FirConverter : public AbstractConverter { // // AbstractConverter overrides - M::Value genExprAddr( - const SomeExpr &expr, M::Location *loc = nullptr) override final { + M::Value genExprAddr(const SomeExpr &expr, + M::Location *loc = nullptr) override final { return createFIRAddr(loc ? *loc : toLocation(), &expr); } - M::Value genExprValue( - const SomeExpr &expr, M::Location *loc = nullptr) override final { + M::Value genExprValue(const SomeExpr &expr, + M::Location *loc = nullptr) override final { return createFIRExpr(loc ? *loc : toLocation(), &expr); } @@ -1239,8 +1264,8 @@ class FirConverter : public AbstractConverter { if (loc.has_value()) { // loc is a pair (begin, end); use the beginning position auto &filePos{loc->first}; - return M::FileLineColLoc::get( - filePos.file.path(), filePos.line, filePos.column, &mlirContext); + return M::FileLineColLoc::get(filePos.file.path(), filePos.line, + filePos.column, &mlirContext); } } return genLocation(); @@ -1254,10 +1279,10 @@ class FirConverter : public AbstractConverter { } }; -} // namespace +} // namespace -void Br::BurnsideBridge::lower( - const Pa::Program &prg, fir::NameUniquer &uniquer) { +void Br::BurnsideBridge::lower(const Pa::Program &prg, + fir::NameUniquer &uniquer) { AST::Program *ast{Br::createAST(prg)}; Br::annotateControl(*ast); if (ClDumpPreFir) { @@ -1277,7 +1302,7 @@ void Br::BurnsideBridge::parseSourceFile(L::SourceMgr &srcMgr) { Br::BurnsideBridge::BurnsideBridge( const Co::IntrinsicTypeDefaultKinds &defaultKinds, const Pa::CookedSource *cooked) - : defaultKinds{defaultKinds}, cooked{cooked} { + : defaultKinds{defaultKinds}, cooked{cooked} { context = std::make_unique(); module = std::make_unique( M::ModuleOp::create(M::UnknownLoc::get(context.get()))); diff --git a/lib/lower/bridge.h b/lib/lower/bridge.h index eab05f4d0123..3272d5319aaf 100644 --- a/lib/lower/bridge.h +++ b/lib/lower/bridge.h @@ -22,27 +22,29 @@ namespace Fortran { namespace common { class IntrinsicTypeDefaultKinds; -template class Reference; -} +template +class Reference; +} // namespace common namespace evaluate { struct DataRef; -template class Expr; +template +class Expr; struct SomeType; -} +} // namespace evaluate namespace parser { class CharBlock; class CookedSource; struct Program; -} +} // namespace parser namespace semantics { class Symbol; } -} // namespace Fortran +} // namespace Fortran namespace llvm { class Module; class SourceMgr; -} +} // namespace llvm namespace mlir { class OpBuilder; } @@ -63,11 +65,11 @@ class AbstractConverter { // Expressions /// Generate the address of the location holding the expression - virtual mlir::Value genExprAddr( - const SomeExpr &, mlir::Location *loc = nullptr) = 0; + virtual mlir::Value genExprAddr(const SomeExpr &, + mlir::Location *loc = nullptr) = 0; /// Generate the computations of the expression to produce a value - virtual mlir::Value genExprValue( - const SomeExpr &, mlir::Location *loc = nullptr) = 0; + virtual mlir::Value genExprValue(const SomeExpr &, + mlir::Location *loc = nullptr) = 0; // // Types @@ -108,9 +110,9 @@ class AbstractConverter { class BurnsideBridge { public: - static BurnsideBridge create( - const common::IntrinsicTypeDefaultKinds &defaultKinds, - const parser::CookedSource *cooked) { + static BurnsideBridge + create(const common::IntrinsicTypeDefaultKinds &defaultKinds, + const parser::CookedSource *cooked) { return BurnsideBridge{defaultKinds, cooked}; } @@ -132,7 +134,7 @@ class BurnsideBridge { private: explicit BurnsideBridge(const common::IntrinsicTypeDefaultKinds &defaultKinds, - const parser::CookedSource *cooked); + const parser::CookedSource *cooked); BurnsideBridge() = delete; BurnsideBridge(const BurnsideBridge &) = delete; @@ -142,6 +144,6 @@ class BurnsideBridge { std::unique_ptr module; }; -} // Fortran::lower +} // namespace Fortran::lower -#endif // FORTRAN_LOWER_BRIDGE_H_ +#endif // FORTRAN_LOWER_BRIDGE_H_ diff --git a/lib/lower/builder.cc b/lib/lower/builder.cc index 61ca189974bb..e73168d1ae72 100644 --- a/lib/lower/builder.cc +++ b/lib/lower/builder.cc @@ -9,10 +9,10 @@ #include "builder.h" #include "bridge.h" #include "convert-type.h" -#include "fir/FIROpsSupport.h" -#include "llvm/ADT/StringRef.h" #include "mlir/IR/Module.h" #include "mlir/IR/Value.h" +#include "optimizer/FIROpsSupport.h" +#include "llvm/ADT/StringRef.h" namespace B = Fortran::lower; namespace Ev = Fortran::evaluate; @@ -23,15 +23,15 @@ using namespace Fortran; using namespace Fortran::lower; M::FuncOp B::createFunction(B::AbstractConverter &converter, - llvm::StringRef name, M::FunctionType funcTy) { - return fir::createFuncOp( - converter.getCurrentLocation(), converter.getModuleOp(), name, funcTy); + llvm::StringRef name, M::FunctionType funcTy) { + return fir::createFuncOp(converter.getCurrentLocation(), + converter.getModuleOp(), name, funcTy); } -M::FuncOp B::createFunction( - M::ModuleOp module, llvm::StringRef name, M::FunctionType funcTy) { - return fir::createFuncOp( - M::UnknownLoc::get(module.getContext()), module, name, funcTy); +M::FuncOp B::createFunction(M::ModuleOp module, llvm::StringRef name, + M::FunctionType funcTy) { + return fir::createFuncOp(M::UnknownLoc::get(module.getContext()), module, + name, funcTy); } M::FuncOp B::getNamedFunction(M::ModuleOp module, llvm::StringRef name) { diff --git a/lib/lower/builder.h b/lib/lower/builder.h index aa0cb78373fe..2d96e3462f4f 100644 --- a/lib/lower/builder.h +++ b/lib/lower/builder.h @@ -10,10 +10,10 @@ #define FORTRAN_LOWER_BUILDER_H_ #include "../semantics/symbol.h" -#include "llvm/ADT/DenseMap.h" #include "mlir/IR/Builders.h" #include "mlir/IR/Function.h" #include "mlir/IR/Module.h" +#include "llvm/ADT/DenseMap.h" #include namespace llvm { @@ -84,14 +84,14 @@ mlir::FuncOp getNamedFunction(mlir::ModuleOp, llvm::StringRef name); /// Create a new FuncOp mlir::FuncOp createFunction(AbstractConverter &converter, llvm::StringRef name, - mlir::FunctionType funcTy); + mlir::FunctionType funcTy); /// Create a new FuncOp /// The function is created with no Location information -mlir::FuncOp createFunction( - mlir::ModuleOp module, llvm::StringRef name, mlir::FunctionType funcTy); +mlir::FuncOp createFunction(mlir::ModuleOp module, llvm::StringRef name, + mlir::FunctionType funcTy); -} // lower -} // Fortran +} // namespace lower +} // namespace Fortran -#endif // FORTRAN_LOWER_BUILDER_H_ +#endif // FORTRAN_LOWER_BUILDER_H_ diff --git a/lib/lower/cfg-builder.h b/lib/lower/cfg-builder.h index cbae0da3a08e..1ab88c15a109 100644 --- a/lib/lower/cfg-builder.h +++ b/lib/lower/cfg-builder.h @@ -60,7 +60,7 @@ class CfgBuilder { }, [](auto) { /* do nothing */ }, }, - e.u); + e.u); if (e.subs) { cacheAssigns(*e.subs); } @@ -88,15 +88,24 @@ class CfgBuilder { return false; } switch (e.cfg) { - case AST::CFGAnnotation::None: break; - case AST::CFGAnnotation::CondGoto: break; - case AST::CFGAnnotation::Iterative: break; - case AST::CFGAnnotation::FirStructuredOp: break; - case AST::CFGAnnotation::IndGoto: return false; - case AST::CFGAnnotation::IoSwitch: return false; - case AST::CFGAnnotation::Switch: return false; - case AST::CFGAnnotation::Return: return false; - case AST::CFGAnnotation::Terminate: return false; + case AST::CFGAnnotation::None: + break; + case AST::CFGAnnotation::CondGoto: + break; + case AST::CFGAnnotation::Iterative: + break; + case AST::CFGAnnotation::FirStructuredOp: + break; + case AST::CFGAnnotation::IndGoto: + return false; + case AST::CFGAnnotation::IoSwitch: + return false; + case AST::CFGAnnotation::Switch: + return false; + case AST::CFGAnnotation::Return: + return false; + case AST::CFGAnnotation::Terminate: + return false; case AST::CFGAnnotation::Goto: if (!std::holds_alternative(e.u)) { return false; @@ -136,7 +145,7 @@ class CfgBuilder { cfgEdgeSetPool.emplace_back(std::move(sink)); auto rc{cfgMap.try_emplace(src, &cfgEdgeSetPool.back())}; assert(rc.second && "insert failed unexpectedly"); - (void)rc; // for release build + (void)rc; // for release build return; } for (auto *s : *iter->second) @@ -153,7 +162,8 @@ class CfgBuilder { } /// Find the next ELSE IF, ELSE or END IF statement in the list - template A nextFalseTarget(A iter, const A &endi) { + template + A nextFalseTarget(A iter, const A &endi) { for (; iter != endi; ++iter) if (std::visit(Co::visitors{ [&](const Pa::ElseIfStmt *) { return true; }, @@ -161,7 +171,7 @@ class CfgBuilder { [&](const Pa::EndIfStmt *) { return true; }, [](auto) { return false; }, }, - iter->u)) { + iter->u)) { break; } return iter; @@ -170,9 +180,9 @@ class CfgBuilder { /// Add branches for this IF block like construct. /// Branch to the "true block", the "false block", and from the end of the /// true block to the end of the construct. - template + template void doNextIfBlock(std::list &evals, AST::Evaluation &e, - const A &iter, const A &endif) { + const A &iter, const A &endif) { A i{iter}; A j{nextFalseTarget(++i, endif)}; auto *cstr = std::get(e.parent); @@ -244,24 +254,24 @@ class CfgBuilder { [](const Pa::MaskedElsewhereStmt *) { TODO(); }, [](auto) { assert(false && "unhandled CGOTO case"); }, }, - e.u); + e.u); break; case AST::CFGAnnotation::IndGoto: - std::visit( - Co::visitors{ - [&](const Pa::AssignedGotoStmt *stmt) { - auto *sym = std::get(stmt->t).symbol; - if (assignedGotoMap.find(sym) != assignedGotoMap.end()) - for (auto *x : assignedGotoMap[sym]) { - addSourceToSink(&e, x); - } - for (auto &l : std::get>(stmt->t)) { - addSourceToSink(&e, l); - } - }, - [](auto) { assert(false && "unhandled IGOTO case"); }, - }, - e.u); + std::visit(Co::visitors{ + [&](const Pa::AssignedGotoStmt *stmt) { + auto *sym = std::get(stmt->t).symbol; + if (assignedGotoMap.find(sym) != assignedGotoMap.end()) + for (auto *x : assignedGotoMap[sym]) { + addSourceToSink(&e, x); + } + for (auto &l : + std::get>(stmt->t)) { + addSourceToSink(&e, l); + } + }, + [](auto) { assert(false && "unhandled IGOTO case"); }, + }, + e.u); break; case AST::CFGAnnotation::IoSwitch: std::visit( @@ -290,7 +300,7 @@ class CfgBuilder { [](const Pa::SelectTypeStmt *) { TODO(); }, [](auto) { assert(false && "unhandled switch case"); }, }, - e.u); + e.u); break; case AST::CFGAnnotation::Iterative: std::visit(Co::visitors{ @@ -301,7 +311,7 @@ class CfgBuilder { [](const Pa::ForallConstructStmt *) { TODO(); }, [](auto) { assert(false && "unhandled loop case"); }, }, - e.u); + e.u); break; case AST::CFGAnnotation::FirStructuredOp: // do not visit the subs @@ -334,7 +344,7 @@ class CfgBuilder { public: CfgBuilder(CFGMapType &m, std::list &p) - : cfgMap{m}, cfgEdgeSetPool{p} {} + : cfgMap{m}, cfgEdgeSetPool{p} {} void run(AST::FunctionLikeUnit &func) { resetPotentialTargets(func.evals); @@ -345,4 +355,4 @@ class CfgBuilder { } }; -#endif // FORTRAN_LOWER_BRIDGE_CFG_BUILDER_H_ +#endif // FORTRAN_LOWER_BRIDGE_CFG_BUILDER_H_ diff --git a/lib/lower/complex-handler.h b/lib/lower/complex-handler.h index cc77d96c40c3..2b310eb9f5d1 100644 --- a/lib/lower/complex-handler.h +++ b/lib/lower/complex-handler.h @@ -12,8 +12,8 @@ /// [Coding style](https://llvm.org/docs/CodingStandards.html) #include "convert-type.h" -#include "fir/FIROps.h" -#include "fir/FIRType.h" +#include "optimizer/FIROps.h" +#include "optimizer/FIRType.h" namespace Fortran::lower { /// Provide helpers to generate Complex manipulations in FIR. @@ -30,8 +30,8 @@ class ComplexHandler { return convertReal(builder.getContext(), complexKind); } - mlir::Value createComplex( - fir::KindTy kind, mlir::Value real, mlir::Value imag) { + mlir::Value createComplex(fir::KindTy kind, mlir::Value real, + mlir::Value imag) { mlir::Type complexTy{fir::CplxType::get(builder.getContext(), kind)}; mlir::Value und = builder.create(loc, complexTy); return insert(insert(und, real), imag); @@ -46,29 +46,31 @@ class ComplexHandler { return getComplexPartType(cplx->getType()); } - template mlir::Value extract(mlir::Value cplx) { - return builder.create( - loc, getComplexPartType(cplx), cplx, getPartId()); + template + mlir::Value extract(mlir::Value cplx) { + return builder.create(loc, getComplexPartType(cplx), + cplx, getPartId()); } - template mlir::Value insert(mlir::Value cplx, mlir::Value part) { + template + mlir::Value insert(mlir::Value cplx, mlir::Value part) { assert(cplx != nullptr); - return builder.create( - loc, cplx->getType(), cplx, part, getPartId()); + return builder.create(loc, cplx->getType(), cplx, part, + getPartId()); } /// Complex part access helper dynamic versions mlir::Value extractComplexPart(mlir::Value cplx, bool isImagPart) { return isImagPart ? extract(cplx) : extract(cplx); } - mlir::Value insertComplexPart( - mlir::Value cplx, mlir::Value part, bool isImagPart) { + mlir::Value insertComplexPart(mlir::Value cplx, mlir::Value part, + bool isImagPart) { return isImagPart ? insert(cplx, part) : insert(cplx, part); } // Complex operation helpers - mlir::Value createComplexCompare( - mlir::Value cplx1, mlir::Value cplx2, bool eq) { + mlir::Value createComplexCompare(mlir::Value cplx1, mlir::Value cplx2, + bool eq) { mlir::Value real1 = extract(cplx1); mlir::Value real2 = extract(cplx2); mlir::Value imag1 = extract(cplx1); @@ -88,7 +90,8 @@ class ComplexHandler { private: // Make mlir ConstantOp from template part id. - template inline mlir::Value getPartId() { + template + inline mlir::Value getPartId() { auto type = mlir::IntegerType::get(32, builder.getContext()); auto attr = builder.getIntegerAttr(type, static_cast(partId)); return builder.create(loc, type, attr).getResult(); @@ -98,5 +101,5 @@ class ComplexHandler { mlir::Location loc; }; -} -#endif // FORTRAN_LOWER_COMPLEX_HANDLER_H_ +} // namespace Fortran::lower +#endif // FORTRAN_LOWER_COMPLEX_HANDLER_H_ diff --git a/lib/lower/convert-expr.cc b/lib/lower/convert-expr.cc index da1ed79b8189..03012217f0fb 100644 --- a/lib/lower/convert-expr.cc +++ b/lib/lower/convert-expr.cc @@ -7,11 +7,6 @@ //===----------------------------------------------------------------------===// #include "convert-expr.h" -#include "bridge.h" -#include "builder.h" -#include "complex-handler.h" -#include "convert-type.h" -#include "runtime.h" #include "../common/default-kinds.h" #include "../common/unwrap.h" #include "../evaluate/fold.h" @@ -19,13 +14,10 @@ #include "../semantics/expression.h" #include "../semantics/symbol.h" #include "../semantics/type.h" -#include "fir/FIRDialect.h" -#include "fir/FIROps.h" -#include "fir/FIRType.h" -#include "llvm/ADT/APFloat.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/IR/Type.h" -#include "llvm/Support/raw_ostream.h" +#include "bridge.h" +#include "builder.h" +#include "complex-handler.h" +#include "convert-type.h" #include "mlir/Dialect/AffineOps/AffineOps.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/StandardOps/Ops.h" @@ -36,6 +28,14 @@ #include "mlir/Pass/PassManager.h" #include "mlir/Transforms/DialectConversion.h" #include "mlir/Transforms/Passes.h" +#include "optimizer/FIRDialect.h" +#include "optimizer/FIROps.h" +#include "optimizer/FIRType.h" +#include "runtime.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/raw_ostream.h" namespace Br = Fortran::lower; namespace Co = Fortran::common; @@ -50,8 +50,8 @@ using namespace Fortran::lower; namespace { -#define TODO() \ - assert(false); \ +#define TODO() \ + assert(false); \ return {} /// Lowering of Fortran::evaluate::Expr expressions @@ -73,12 +73,18 @@ class ExprLowering { /// unordered, but we may want to cons ordered in certain situation. static M::CmpIPredicate translateRelational(Co::RelationalOperator rop) { switch (rop) { - case Co::RelationalOperator::LT: return M::CmpIPredicate::slt; - case Co::RelationalOperator::LE: return M::CmpIPredicate::sle; - case Co::RelationalOperator::EQ: return M::CmpIPredicate::eq; - case Co::RelationalOperator::NE: return M::CmpIPredicate::ne; - case Co::RelationalOperator::GT: return M::CmpIPredicate::sgt; - case Co::RelationalOperator::GE: return M::CmpIPredicate::sge; + case Co::RelationalOperator::LT: + return M::CmpIPredicate::slt; + case Co::RelationalOperator::LE: + return M::CmpIPredicate::sle; + case Co::RelationalOperator::EQ: + return M::CmpIPredicate::eq; + case Co::RelationalOperator::NE: + return M::CmpIPredicate::ne; + case Co::RelationalOperator::GT: + return M::CmpIPredicate::sgt; + case Co::RelationalOperator::GE: + return M::CmpIPredicate::sge; } assert(false && "unhandled INTEGER relational operator"); return {}; @@ -92,22 +98,28 @@ class ExprLowering { /// FIXME: The signaling/quiet aspect of the table 17.1 requirement is not /// fully enforced. FIR and LLVM `fcmp` instructions do not give any guarantee /// whether the comparison will signal or not in case of quiet NaN argument. - static fir::CmpFPredicate translateFloatRelational( - Co::RelationalOperator rop) { + static fir::CmpFPredicate + translateFloatRelational(Co::RelationalOperator rop) { switch (rop) { - case Co::RelationalOperator::LT: return fir::CmpFPredicate::OLT; - case Co::RelationalOperator::LE: return fir::CmpFPredicate::OLE; - case Co::RelationalOperator::EQ: return fir::CmpFPredicate::OEQ; - case Co::RelationalOperator::NE: return fir::CmpFPredicate::UNE; - case Co::RelationalOperator::GT: return fir::CmpFPredicate::OGT; - case Co::RelationalOperator::GE: return fir::CmpFPredicate::OGE; + case Co::RelationalOperator::LT: + return fir::CmpFPredicate::OLT; + case Co::RelationalOperator::LE: + return fir::CmpFPredicate::OLE; + case Co::RelationalOperator::EQ: + return fir::CmpFPredicate::OEQ; + case Co::RelationalOperator::NE: + return fir::CmpFPredicate::UNE; + case Co::RelationalOperator::GT: + return fir::CmpFPredicate::OGT; + case Co::RelationalOperator::GE: + return fir::CmpFPredicate::OGE; } assert(false && "unhandled REAL relational operator"); return {}; } /// Generate an integral constant of `value` - template + template M::Value genIntegerConstant(M::MLIRContext *context, std::int64_t value) { M::Type type{converter.genType(IntegerCat, KIND)}; auto attr{builder.getIntegerAttr(type, value)}; @@ -122,7 +134,7 @@ class ExprLowering { return builder.create(getLoc(), i1Type, attr).getResult(); } - template + template M::Value genRealConstant(M::MLIRContext *context, L::APFloat const &value) { M::Type fltTy{convertReal(context, KIND)}; auto attr{builder.getFloatAttr(fltTy, value)}; @@ -134,17 +146,18 @@ class ExprLowering { return M::IndexType::get(builder.getContext()); } - template + template M::Value createBinaryOp(A const &ex, M::Value lhs, M::Value rhs) { assert(lhs && rhs && "argument did not lower"); auto x = builder.create(getLoc(), lhs, rhs); return x.getResult(); } - template + template M::Value createBinaryOp(A const &ex, M::Value rhs) { return createBinaryOp(ex, genval(ex.left()), rhs); } - template M::Value createBinaryOp(A const &ex) { + template + M::Value createBinaryOp(A const &ex) { return createBinaryOp(ex, genval(ex.left()), genval(ex.right())); } @@ -152,7 +165,7 @@ class ExprLowering { auto module{getModule(&builder)}; if (M::FuncOp func{getNamedFunction(module, name)}) { assert(func.getType() == funTy && - "function already declared with a different type"); + "function already declared with a different type"); return func; } return createFunction(module, name, funTy); @@ -164,7 +177,8 @@ class ExprLowering { } // FIXME binary operation :: ('a, 'a) -> 'a - template M::FunctionType createFunctionType() { + template + M::FunctionType createFunctionType() { if constexpr (TC == IntegerCat) { M::Type output{converter.genType(IntegerCat, KIND)}; L::SmallVector inputs; @@ -184,7 +198,7 @@ class ExprLowering { } /// Create a call to a Fortran runtime entry point - template + template M::Value createBinaryFIRTCall(A const &ex, RuntimeEntryCode callee) { L::SmallVector operands; operands.push_back(genval(ex.left())); @@ -192,38 +206,38 @@ class ExprLowering { M::FunctionType funTy = createFunctionType(); auto func{getRuntimeFunction(callee, funTy)}; auto x{builder.create(getLoc(), func, operands)}; - return x.getResult(0); // FIXME + return x.getResult(0); // FIXME } - template - M::Value createCompareOp( - A const &ex, M::CmpIPredicate pred, M::Value lhs, M::Value rhs) { + template + M::Value createCompareOp(A const &ex, M::CmpIPredicate pred, M::Value lhs, + M::Value rhs) { assert(lhs && rhs && "argument did not lower"); auto x = builder.create(getLoc(), pred, lhs, rhs); return x.getResult(); } - template + template M::Value createCompareOp(A const &ex, M::CmpIPredicate pred) { - return createCompareOp( - ex, pred, genval(ex.left()), genval(ex.right())); + return createCompareOp(ex, pred, genval(ex.left()), + genval(ex.right())); } - template - M::Value createFltCmpOp( - A const &ex, fir::CmpFPredicate pred, M::Value lhs, M::Value rhs) { + template + M::Value createFltCmpOp(A const &ex, fir::CmpFPredicate pred, M::Value lhs, + M::Value rhs) { assert(lhs && rhs && "argument did not lower"); auto x = builder.create(getLoc(), pred, lhs, rhs); return x.getResult(); } - template + template M::Value createFltCmpOp(A const &ex, fir::CmpFPredicate pred) { - return createFltCmpOp( - ex, pred, genval(ex.left()), genval(ex.right())); + return createFltCmpOp(ex, pred, genval(ex.left()), + genval(ex.right())); } M::Value gen(Se::SymbolRef sym) { // FIXME: not all symbols are local - M::Value addr = createTemporary( - getLoc(), builder, symMap, converter.genType(sym), &*sym); + M::Value addr = createTemporary(getLoc(), builder, symMap, + converter.genType(sym), &*sym); assert(addr && "failed generating symbol address"); // Get address from descriptor if symbol has one. auto type{addr->getType()}; @@ -267,16 +281,18 @@ class ExprLowering { M::Value genval(Ev::StructureConstructor const &) { TODO(); } M::Value genval(Ev::ImpliedDoIndex const &) { TODO(); } M::Value genval(Ev::DescriptorInquiry const &) { TODO(); } - template M::Value genval(Ev::TypeParamInquiry const &) { + template + M::Value genval(Ev::TypeParamInquiry const &) { TODO(); } - template M::Value genval(Ev::ComplexComponent const &part) { + template + M::Value genval(Ev::ComplexComponent const &part) { return ComplexHandler{builder, getLoc()}.extractComplexPart( genval(part.left()), part.isImaginaryPart); } - template + template M::Value genval(Ev::Negate> const &op) { auto input{genval(op.left())}; if constexpr (TC == IntegerCat) { @@ -291,7 +307,7 @@ class ExprLowering { } } - template + template M::Value genval(Ev::Add> const &op) { if constexpr (TC == IntegerCat) { return createBinaryOp(op); @@ -302,7 +318,7 @@ class ExprLowering { return createBinaryOp(op); } } - template + template M::Value genval(Ev::Subtract> const &op) { if constexpr (TC == IntegerCat) { return createBinaryOp(op); @@ -313,7 +329,7 @@ class ExprLowering { return createBinaryOp(op); } } - template + template M::Value genval(Ev::Multiply> const &op) { if constexpr (TC == IntegerCat) { return createBinaryOp(op); @@ -324,7 +340,7 @@ class ExprLowering { return createBinaryOp(op); } } - template + template M::Value genval(Ev::Divide> const &op) { if constexpr (TC == IntegerCat) { return createBinaryOp(op); @@ -335,34 +351,36 @@ class ExprLowering { return createBinaryOp(op); } } - template + template M::Value genval(Ev::Power> const &op) { - llvm::SmallVector operands{ - genval(op.left()), genval(op.right())}; + llvm::SmallVector operands{genval(op.left()), + genval(op.right())}; M::Type ty{converter.genType(TC, KIND)}; return intrinsics.genval(getLoc(), builder, "pow", ty, operands); } - template + template M::Value genval(Ev::RealToIntPower> const &op) { // TODO: runtime as limited integer kind support. Look if the conversions // are ok - llvm::SmallVector operands{ - genval(op.left()), genval(op.right())}; + llvm::SmallVector operands{genval(op.left()), + genval(op.right())}; M::Type ty{converter.genType(TC, KIND)}; return intrinsics.genval(getLoc(), builder, "pow", ty, operands); } - template M::Value genval(Ev::ComplexConstructor const &op) { + template + M::Value genval(Ev::ComplexConstructor const &op) { return ComplexHandler{builder, getLoc()}.createComplex( KIND, genval(op.left()), genval(op.right())); } - template M::Value genval(Ev::Concat const &op) { + template + M::Value genval(Ev::Concat const &op) { // TODO this is a bogus call return createBinaryFIRTCall(op, FIRT_CONCAT); } /// MIN and MAX operations - template + template M::Value genval(Ev::Extremum> const &op) { if constexpr (TC == IntegerCat) { return createBinaryFIRTCall( @@ -372,9 +390,12 @@ class ExprLowering { } } - template M::Value genval(Ev::SetLength const &) { TODO(); } + template + M::Value genval(Ev::SetLength const &) { + TODO(); + } - template + template M::Value genval(Ev::Relational> const &op) { mlir::Value result{nullptr}; if constexpr (TC == IntegerCat) { @@ -384,9 +405,8 @@ class ExprLowering { createFltCmpOp(op, translateFloatRelational(op.opr)); } else if constexpr (TC == ComplexCat) { bool eq{op.opr == Co::RelationalOperator::EQ}; - assert(eq || - op.opr == Co::RelationalOperator::NE && - "relation undefined for complex"); + assert(eq || op.opr == Co::RelationalOperator::NE && + "relation undefined for complex"); result = ComplexHandler{builder, getLoc()}.createComplexCompare( genval(op.left()), genval(op.right()), eq); } else { @@ -400,7 +420,7 @@ class ExprLowering { return std::visit([&](const auto &x) { return genval(x); }, op.u); } - template + template M::Value genval(Ev::Convert, TC2> const &convert) { auto ty{converter.genType(TC1, KIND)}; M::Value operand{genval(convert.left())}; @@ -412,9 +432,13 @@ class ExprLowering { return builder.create(getLoc(), ty, operand); } - template M::Value genval(Ev::Parentheses const &) { TODO(); } + template + M::Value genval(Ev::Parentheses const &) { + TODO(); + } - template M::Value genval(const Ev::Not &op) { + template + M::Value genval(const Ev::Not &op) { // Request operands to be generated as `i1` and restore after this scope. auto restorer{common::ScopedSet(genLogicalAsI1, true)}; auto *context{builder.getContext()}; @@ -423,13 +447,18 @@ class ExprLowering { return builder.create(getLoc(), logical, one).getResult(); } - template M::Value genval(Ev::LogicalOperation const &op) { + template + M::Value genval(Ev::LogicalOperation const &op) { // Request operands to be generated as `i1` and restore after this scope. auto restorer{common::ScopedSet(genLogicalAsI1, true)}; mlir::Value result; switch (op.logicalOperator) { - case Ev::LogicalOperator::And: result = createBinaryOp(op); break; - case Ev::LogicalOperator::Or: result = createBinaryOp(op); break; + case Ev::LogicalOperator::And: + result = createBinaryOp(op); + break; + case Ev::LogicalOperator::Or: + result = createBinaryOp(op); + break; case Ev::LogicalOperator::Eqv: result = createCompareOp(op, M::CmpIPredicate::eq); break; @@ -447,7 +476,7 @@ class ExprLowering { return result; } - template + template M::Value genval(Ev::Constant> const &con) { // TODO: // - character type constant @@ -505,7 +534,7 @@ class ExprLowering { } } - template + template M::Value genval(Ev::Constant> const &con) { if constexpr (TC == IntegerCat) { auto opt = (*con).ToInt64(); @@ -519,7 +548,8 @@ class ExprLowering { } } - template M::Value genval(Ev::ArrayConstructor const &) { + template + M::Value genval(Ev::ArrayConstructor const &) { TODO(); } M::Value gen(Ev::ComplexPart const &) { TODO(); } @@ -537,7 +567,7 @@ class ExprLowering { }, [&](const Ev::Triplet &x) { return genval(x); }, }, - subs.u); + subs.u); } M::Value gen(const Ev::DataRef &dref) { @@ -550,8 +580,9 @@ class ExprLowering { // Helper function to turn the left-recursive Component structure into a list. // Returns the object used as the base coordinate for the component chain. - static Ev::DataRef const *reverseComponents( - Ev::Component const &cmpt, std::list &list) { + static Ev::DataRef const * + reverseComponents(Ev::Component const &cmpt, + std::list &list) { list.push_front(&cmpt); return std::visit( Co::visitors{ @@ -634,27 +665,33 @@ class ExprLowering { return builder.create(getLoc(), gen(coref)); } - template M::Value gen(Ev::Designator const &des) { + template + M::Value gen(Ev::Designator const &des) { return std::visit([&](const auto &x) { return gen(x); }, des.u); } - template M::Value gendef(Ev::Designator const &des) { + template + M::Value gendef(Ev::Designator const &des) { return gen(des); } - template M::Value genval(Ev::Designator const &des) { + template + M::Value genval(Ev::Designator const &des) { return std::visit([&](const auto &x) { return genval(x); }, des.u); } // call a function - template M::Value gen(const Ev::FunctionRef &funRef) { + template + M::Value gen(const Ev::FunctionRef &funRef) { TODO(); } - template M::Value gendef(const Ev::FunctionRef &funRef) { + template + M::Value gendef(const Ev::FunctionRef &funRef) { return gen(funRef); } - template M::Value genval(const Ev::FunctionRef &funRef) { - TODO(); // Derived type functions (user + intrinsics) + template + M::Value genval(const Ev::FunctionRef &funRef) { + TODO(); // Derived type functions (user + intrinsics) } - template + template M::Value genval(const Ev::FunctionRef> &funRef) { if (const auto &intrinsic{funRef.proc().GetSpecificIntrinsic()}) { M::Type ty{converter.genType(TC, KIND)}; @@ -670,7 +707,7 @@ class ExprLowering { if (auto *expr{Ev::UnwrapExpr>(arg)}) { operands.push_back(genval(*expr)); } else { - operands.push_back(nullptr); // optional + operands.push_back(nullptr); // optional } } // Let the intrinsic library lower the intrinsic function call @@ -685,8 +722,8 @@ class ExprLowering { // and not `i1`. auto restorer{common::ScopedSet(genLogicalAsI1, false)}; for (const auto &arg : funRef.arguments()) { - assert( - arg.has_value() && "optional argument requires explicit interface"); + assert(arg.has_value() && + "optional argument requires explicit interface"); const auto *expr{arg->UnwrapExpr()}; assert(expr && "assumed type argument requires explicit interface"); if (const Se::Symbol * sym{Ev::UnwrapWholeSymbolDataRef(*expr)}) { @@ -707,23 +744,26 @@ class ExprLowering { // For now, Fortran return value are implemented with a single MLIR // function return value. assert(call.getNumResults() == 1 && - "Expected exactly one result in FUNCTION call"); + "Expected exactly one result in FUNCTION call"); return call.getResult(0); } } - template M::Value gen(const Ev::Expr &exp) { + template + M::Value gen(const Ev::Expr &exp) { // must be a designator or function-reference (R902) return std::visit([&](const auto &e) { return gendef(e); }, exp.u); } - template M::Value gendef(Ev::Expr const &exp) { + template + M::Value gendef(Ev::Expr const &exp) { return gen(exp); } - template M::Value genval(Ev::Expr const &exp) { + template + M::Value genval(Ev::Expr const &exp) { return std::visit([&](const auto &e) { return genval(e); }, exp.u); } - template + template M::Value genval(Ev::Expr> const &exp) { auto result{std::visit([&](const auto &e) { return genval(e); }, exp.u)}; // Handle the `i1` to `fir.logical` conversions as needed. @@ -751,7 +791,8 @@ class ExprLowering { return result; } - template M::Value gendef(const A &) { + template + M::Value gendef(const A &) { assert(false && "expression error"); return {}; } @@ -761,46 +802,53 @@ class ExprLowering { return converter.mangleName(*symbol); // Do not mangle intrinsic for now assert(proc.GetSpecificIntrinsic() && - "expected intrinsic procedure in designator"); + "expected intrinsic procedure in designator"); return proc.GetName(); } public: explicit ExprLowering(M::Location loc, AbstractConverter &converter, - SomeExpr const &vop, SymMap &map, IntrinsicLibrary const &intr, - bool logicalAsI1 = false) - : location{loc}, converter{converter}, builder{converter.getOpBuilder()}, - expr{vop}, symMap{map}, intrinsics{intr}, genLogicalAsI1{logicalAsI1} {} + SomeExpr const &vop, SymMap &map, + IntrinsicLibrary const &intr, bool logicalAsI1 = false) + : location{loc}, converter{converter}, builder{converter.getOpBuilder()}, + expr{vop}, symMap{map}, intrinsics{intr}, genLogicalAsI1{logicalAsI1} {} /// Lower the expression `expr` into MLIR standard dialect M::Value gen() { return gen(expr); } M::Value genval() { return genval(expr); } }; -} // namespace +} // namespace M::Value Br::createSomeExpression(M::Location loc, - Br::AbstractConverter &converter, const Ev::Expr &expr, - SymMap &symMap, const IntrinsicLibrary &intrinsics) { + Br::AbstractConverter &converter, + const Ev::Expr &expr, + SymMap &symMap, + const IntrinsicLibrary &intrinsics) { return ExprLowering{loc, converter, expr, symMap, intrinsics, false}.genval(); } M::Value Br::createI1LogicalExpression(M::Location loc, - Br::AbstractConverter &converter, const Ev::Expr &expr, - SymMap &symMap, const IntrinsicLibrary &intrinsics) { + Br::AbstractConverter &converter, + const Ev::Expr &expr, + SymMap &symMap, + const IntrinsicLibrary &intrinsics) { return ExprLowering{loc, converter, expr, symMap, intrinsics, true}.genval(); } M::Value Br::createSomeAddress(M::Location loc, - Br::AbstractConverter &converter, const Ev::Expr &expr, - SymMap &symMap, const IntrinsicLibrary &intrinsics) { + Br::AbstractConverter &converter, + const Ev::Expr &expr, + SymMap &symMap, + const IntrinsicLibrary &intrinsics) { return ExprLowering{loc, converter, expr, symMap, intrinsics}.gen(); } /// Create a temporary variable /// `symbol` will be nullptr for an anonymous temporary M::Value Br::createTemporary(M::Location loc, M::OpBuilder &builder, - SymMap &symMap, M::Type type, const Se::Symbol *symbol) { + SymMap &symMap, M::Type type, + const Se::Symbol *symbol) { if (symbol) if (auto val = symMap.lookupSymbol(*symbol)) { if (auto op = val->getDefiningOp()) { diff --git a/lib/lower/convert-expr.h b/lib/lower/convert-expr.h index b1d6469585a4..8ca0a7c77b00 100644 --- a/lib/lower/convert-expr.h +++ b/lib/lower/convert-expr.h @@ -18,25 +18,26 @@ class Location; class OpBuilder; class Type; class Value; -} // mlir +} // namespace mlir namespace fir { class AllocaExpr; -} // fir +} // namespace fir namespace Fortran { namespace common { class IntrinsicTypeDefaultKinds; -} // common +} // namespace common namespace evaluate { -template class Expr; +template +class Expr; struct SomeType; -} // evaluate +} // namespace evaluate namespace semantics { class Symbol; -} // semantics +} // namespace semantics namespace lower { @@ -44,23 +45,26 @@ class AbstractConverter; class SymMap; mlir::Value createSomeExpression(mlir::Location loc, - AbstractConverter &converter, - const evaluate::Expr &expr, SymMap &symMap, - const IntrinsicLibrary &intrinsics); + AbstractConverter &converter, + const evaluate::Expr &expr, + SymMap &symMap, + const IntrinsicLibrary &intrinsics); -mlir::Value createI1LogicalExpression(mlir::Location loc, - AbstractConverter &converter, - const evaluate::Expr &expr, SymMap &symMap, - const IntrinsicLibrary &intrinsics); +mlir::Value +createI1LogicalExpression(mlir::Location loc, AbstractConverter &converter, + const evaluate::Expr &expr, + SymMap &symMap, const IntrinsicLibrary &intrinsics); mlir::Value createSomeAddress(mlir::Location loc, AbstractConverter &converter, - const evaluate::Expr &expr, SymMap &symMap, - const IntrinsicLibrary &intrinsics); + const evaluate::Expr &expr, + SymMap &symMap, + const IntrinsicLibrary &intrinsics); mlir::Value createTemporary(mlir::Location loc, mlir::OpBuilder &builder, - SymMap &symMap, mlir::Type type, const semantics::Symbol *symbol); + SymMap &symMap, mlir::Type type, + const semantics::Symbol *symbol); -} // lower -} // Fortran +} // namespace lower +} // namespace Fortran -#endif // FORTRAN_LOWER_CONVERT_EXPR_H_ +#endif // FORTRAN_LOWER_CONVERT_EXPR_H_ diff --git a/lib/lower/convert-type.cc b/lib/lower/convert-type.cc index 3ec61b744a7c..f44bcebedc37 100644 --- a/lib/lower/convert-type.cc +++ b/lib/lower/convert-type.cc @@ -13,15 +13,15 @@ // limitations under the License. #include "convert-type.h" -#include "bridge.h" #include "../semantics/expression.h" #include "../semantics/tools.h" #include "../semantics/type.h" -#include "fir/FIRType.h" +#include "bridge.h" #include "mlir/IR/Builders.h" #include "mlir/IR/Location.h" #include "mlir/IR/MLIRContext.h" #include "mlir/IR/StandardTypes.h" +#include "optimizer/FIRType.h" namespace Br = Fortran::lower; namespace Co = Fortran::common; @@ -35,106 +35,131 @@ using namespace Fortran::lower; namespace { -template bool isConstant(const Ev::Expr &e) { +template +bool isConstant(const Ev::Expr &e) { return Ev::IsConstantExpr(SomeExpr{e}); } -template int64_t toConstant(const Ev::Expr &e) { +template +int64_t toConstant(const Ev::Expr &e) { auto opt = Ev::ToInt64(e); assert(opt.has_value() && "expression didn't resolve to a constant"); return opt.value(); } #undef TODO -#define TODO() \ - assert(false && "not yet implemented"); \ +#define TODO() \ + assert(false && "not yet implemented"); \ return {} // one argument template, must be specialized -template M::Type genFIRType(M::MLIRContext *, int) { +template +M::Type genFIRType(M::MLIRContext *, int) { return {}; } // two argument template -template +template M::Type genFIRType(M::MLIRContext *context) { if constexpr (TC == IntegerCat) { auto bits{Ev::Type::Scalar::bits}; return M::IntegerType::get(bits, context); } else if constexpr (TC == LogicalCat || TC == CharacterCat || - TC == ComplexCat) { + TC == ComplexCat) { return genFIRType(context, KIND); } else { return {}; } } -template<> M::Type genFIRType(M::MLIRContext *context) { +template <> +M::Type genFIRType(M::MLIRContext *context) { return M::FloatType::getF16(context); } -template<> M::Type genFIRType(M::MLIRContext *context) { +template <> +M::Type genFIRType(M::MLIRContext *context) { return M::FloatType::getBF16(context); } -template<> M::Type genFIRType(M::MLIRContext *context) { +template <> +M::Type genFIRType(M::MLIRContext *context) { return M::FloatType::getF32(context); } -template<> M::Type genFIRType(M::MLIRContext *context) { +template <> +M::Type genFIRType(M::MLIRContext *context) { return M::FloatType::getF64(context); } -template<> M::Type genFIRType(M::MLIRContext *context) { +template <> +M::Type genFIRType(M::MLIRContext *context) { return fir::RealType::get(context, 10); } -template<> M::Type genFIRType(M::MLIRContext *context) { +template <> +M::Type genFIRType(M::MLIRContext *context) { return fir::RealType::get(context, 16); } -template<> M::Type genFIRType(M::MLIRContext *context, int kind) { +template <> +M::Type genFIRType(M::MLIRContext *context, int kind) { if (Ev::IsValidKindOfIntrinsicType(RealCat, kind)) { switch (kind) { - case 2: return genFIRType(context); - case 3: return genFIRType(context); - case 4: return genFIRType(context); - case 8: return genFIRType(context); - case 10: return genFIRType(context); - case 16: return genFIRType(context); + case 2: + return genFIRType(context); + case 3: + return genFIRType(context); + case 4: + return genFIRType(context); + case 8: + return genFIRType(context); + case 10: + return genFIRType(context); + case 16: + return genFIRType(context); } assert(false && "type translation not implemented"); } return {}; } -template<> M::Type genFIRType(M::MLIRContext *context, int kind) { +template <> +M::Type genFIRType(M::MLIRContext *context, int kind) { if (Ev::IsValidKindOfIntrinsicType(IntegerCat, kind)) { switch (kind) { - case 1: return genFIRType(context); - case 2: return genFIRType(context); - case 4: return genFIRType(context); - case 8: return genFIRType(context); - case 16: return genFIRType(context); + case 1: + return genFIRType(context); + case 2: + return genFIRType(context); + case 4: + return genFIRType(context); + case 8: + return genFIRType(context); + case 16: + return genFIRType(context); } assert(false && "type translation not implemented"); } return {}; } -template<> M::Type genFIRType(M::MLIRContext *context, int KIND) { +template <> +M::Type genFIRType(M::MLIRContext *context, int KIND) { if (Ev::IsValidKindOfIntrinsicType(LogicalCat, KIND)) return fir::LogicalType::get(context, KIND); return {}; } -template<> M::Type genFIRType(M::MLIRContext *context, int KIND) { +template <> +M::Type genFIRType(M::MLIRContext *context, int KIND) { if (Ev::IsValidKindOfIntrinsicType(CharacterCat, KIND)) return fir::CharacterType::get(context, KIND); return {}; } -template<> M::Type genFIRType(M::MLIRContext *context, int KIND) { +template <> +M::Type genFIRType(M::MLIRContext *context, int KIND) { if (Ev::IsValidKindOfIntrinsicType(ComplexCat, KIND)) return fir::CplxType::get(context, KIND); return {}; @@ -146,23 +171,32 @@ class TypeBuilder { M::MLIRContext *context; Co::IntrinsicTypeDefaultKinds const &defaults; - template int defaultKind() { return defaultKind(TC); } + template + int defaultKind() { + return defaultKind(TC); + } int defaultKind(Co::TypeCategory TC) { return defaults.GetDefaultKind(TC); } public: - explicit TypeBuilder( - M::MLIRContext *context, Co::IntrinsicTypeDefaultKinds const &defaults) - : context{context}, defaults{defaults} {} + explicit TypeBuilder(M::MLIRContext *context, + Co::IntrinsicTypeDefaultKinds const &defaults) + : context{context}, defaults{defaults} {} // non-template, arguments are runtime values M::Type genFIRTy(Co::TypeCategory tc, int kind) { switch (tc) { - case RealCat: return genFIRType(context, kind); - case IntegerCat: return genFIRType(context, kind); - case ComplexCat: return genFIRType(context, kind); - case LogicalCat: return genFIRType(context, kind); - case CharacterCat: return genFIRType(context, kind); - default: break; + case RealCat: + return genFIRType(context, kind); + case IntegerCat: + return genFIRType(context, kind); + case ComplexCat: + return genFIRType(context, kind); + case LogicalCat: + return genFIRType(context, kind); + case CharacterCat: + return genFIRType(context, kind); + default: + break; } assert(false && "unhandled type category"); return {}; @@ -177,41 +211,45 @@ class TypeBuilder { return genFIRType(context, defaultKind()); } - template typename A, Co::TypeCategory TC> + template