//===-- ConvertExprToHLFIR.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ // //===----------------------------------------------------------------------===// #include "flang/Lower/ConvertExprToHLFIR.h" #include "flang/Evaluate/shape.h" #include "flang/Lower/AbstractConverter.h" #include "flang/Lower/CallInterface.h" #include "flang/Lower/ConvertCall.h" #include "flang/Lower/ConvertConstant.h" #include "flang/Lower/ConvertType.h" #include "flang/Lower/IntrinsicCall.h" #include "flang/Lower/StatementContext.h" #include "flang/Lower/SymbolMap.h" #include "flang/Optimizer/Builder/Complex.h" #include "flang/Optimizer/Builder/Runtime/Character.h" #include "flang/Optimizer/Builder/Todo.h" #include "flang/Optimizer/HLFIR/HLFIROps.h" #include "llvm/ADT/TypeSwitch.h" #include namespace { /// Lower Designators to HLFIR. class HlfirDesignatorBuilder { public: HlfirDesignatorBuilder(mlir::Location loc, Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) : converter{converter}, symMap{symMap}, stmtCtx{stmtCtx}, loc{loc} {} // Character designators variant contains substrings using CharacterDesignators = decltype(Fortran::evaluate::Designator>::u); hlfir::EntityWithAttributes gen(const CharacterDesignators &designatorVariant) { return std::visit( [&](const auto &x) -> hlfir::EntityWithAttributes { return gen(x); }, designatorVariant); } // Character designators variant contains complex parts using RealDesignators = decltype(Fortran::evaluate::Designator>::u); hlfir::EntityWithAttributes gen(const RealDesignators &designatorVariant) { return std::visit( [&](const auto &x) -> hlfir::EntityWithAttributes { return gen(x); }, designatorVariant); } // All other designators are similar using OtherDesignators = decltype(Fortran::evaluate::Designator>::u); hlfir::EntityWithAttributes gen(const OtherDesignators &designatorVariant) { return std::visit( [&](const auto &x) -> hlfir::EntityWithAttributes { return gen(x); }, designatorVariant); } hlfir::EntityWithAttributes gen(const Fortran::evaluate::NamedEntity &namedEntity) { if (namedEntity.IsSymbol()) return gen(Fortran::evaluate::SymbolRef{namedEntity.GetLastSymbol()}); return gen(namedEntity.GetComponent()); } private: /// Struct that is filled while visiting a part-ref (in the "visit" member /// function) before the top level "gen" generates an hlfir.declare for the /// part ref. It contains the lowered pieces of the part-ref that will /// become the operands of an hlfir.declare. struct PartInfo { fir::FortranVariableOpInterface base; hlfir::DesignateOp::Subscripts subscripts; mlir::Value resultShape; llvm::SmallVector typeParams; llvm::SmallVector substring; }; /// Generate an hlfir.declare for a part-ref given a filled PartInfo and the /// FIR type for this part-ref. fir::FortranVariableOpInterface genDeclare(mlir::Type resultValueType, PartInfo &partInfo) { // Compute hlfir.declare result type. // TODO: ensure polymorphic aspect of base of component will be // preserved, as well as pointer/allocatable component aspects. mlir::Type resultType; /// Array sections may be non contiguous, so the output must be a box even /// when the extents are static. This can be refined later for cases where /// the output is know to be simply contiguous and that do not have lower /// bounds. auto charType = resultValueType.dyn_cast(); if (charType && charType.hasDynamicLen()) resultType = fir::BoxCharType::get(charType.getContext(), charType.getFKind()); else if (resultValueType.isa() || fir::hasDynamicSize(resultValueType)) resultType = fir::BoxType::get(resultValueType); else resultType = fir::ReferenceType::get(resultValueType); std::optional complexPart; auto designate = getBuilder().create( getLoc(), resultType, partInfo.base.getBase(), "", /*componentShape=*/mlir::Value{}, partInfo.subscripts, partInfo.substring, complexPart, partInfo.resultShape, partInfo.typeParams); return mlir::cast( designate.getOperation()); } fir::FortranVariableOpInterface gen(const Fortran::evaluate::SymbolRef &symbolRef) { if (std::optional varDef = getSymMap().lookupVariableDefinition(symbolRef)) return *varDef; TODO(getLoc(), "lowering symbol to HLFIR"); } hlfir::EntityWithAttributes gen(const Fortran::evaluate::Component &component) { TODO(getLoc(), "lowering component to HLFIR"); } hlfir::EntityWithAttributes gen(const Fortran::evaluate::ArrayRef &arrayRef) { PartInfo partInfo; mlir::Type resultType = visit(arrayRef, partInfo); return genDeclare(resultType, partInfo); } hlfir::EntityWithAttributes gen(const Fortran::evaluate::CoarrayRef &coarrayRef) { TODO(getLoc(), "lowering CoarrayRef to HLFIR"); } mlir::Type visit(const Fortran::evaluate::CoarrayRef &, PartInfo &) { TODO(getLoc(), "lowering CoarrayRef to HLFIR"); } hlfir::EntityWithAttributes gen(const Fortran::evaluate::ComplexPart &complexPart) { TODO(getLoc(), "lowering complex part to HLFIR"); } hlfir::EntityWithAttributes gen(const Fortran::evaluate::Substring &substring) { PartInfo partInfo; mlir::Type baseStringType = std::visit( [&](const auto &x) { return visit(x, partInfo); }, substring.parent()); assert(partInfo.typeParams.size() == 1 && "expect base string length"); // Compute the substring lower and upper bound. partInfo.substring.push_back(genSubscript(substring.lower())); if (Fortran::evaluate::MaybeExtentExpr upperBound = substring.upper()) partInfo.substring.push_back(genSubscript(*upperBound)); else partInfo.substring.push_back(partInfo.typeParams[0]); fir::FirOpBuilder &builder = getBuilder(); mlir::Location loc = getLoc(); mlir::Type idxTy = builder.getIndexType(); partInfo.substring[0] = builder.createConvert(loc, idxTy, partInfo.substring[0]); partInfo.substring[1] = builder.createConvert(loc, idxTy, partInfo.substring[1]); // Try using constant length if available. mlir::arith folding would // most likely be able to fold "max(ub-lb+1,0)" too, but getting // the constant length in the FIR types would be harder. std::optional cstLen = Fortran::evaluate::ToInt64(Fortran::evaluate::Fold( getConverter().getFoldingContext(), substring.LEN())); if (cstLen) { partInfo.typeParams[0] = builder.createIntegerConstant(loc, idxTy, *cstLen); } else { // Compute "len = max(ub-lb+1,0)" (Fortran 2018 9.4.1). mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); auto boundsDiff = builder.create( loc, partInfo.substring[1], partInfo.substring[0]); auto rawLen = builder.create(loc, boundsDiff, one); partInfo.typeParams[0] = fir::factory::genMaxWithZero(builder, loc, rawLen); } mlir::Type resultType = changeLengthInCharacterType( loc, baseStringType, cstLen ? *cstLen : fir::CharacterType::unknownLen()); return genDeclare(resultType, partInfo); } static mlir::Type changeLengthInCharacterType(mlir::Location loc, mlir::Type type, int64_t newLen) { return llvm::TypeSwitch(type) .Case([&](fir::CharacterType charTy) -> mlir::Type { return fir::CharacterType::get(charTy.getContext(), charTy.getFKind(), newLen); }) .Case([&](fir::SequenceType seqTy) -> mlir::Type { return fir::SequenceType::get( seqTy.getShape(), changeLengthInCharacterType(loc, seqTy.getEleTy(), newLen)); }) .Case([&](auto t) -> mlir::Type { using FIRT = decltype(t); return FIRT::get( changeLengthInCharacterType(loc, t.getEleTy(), newLen)); }) .Default([loc](mlir::Type t) -> mlir::Type { fir::emitFatalError(loc, "expected character type"); }); } mlir::Type visit(const Fortran::evaluate::DataRef &dataRef, PartInfo &partInfo) { return std::visit([&](const auto &x) { return visit(x, partInfo); }, dataRef.u); } mlir::Type visit(const Fortran::evaluate::StaticDataObject::Pointer &staticObject, PartInfo &partInfo) { fir::FirOpBuilder &builder = getBuilder(); mlir::Location loc = getLoc(); std::optional string = staticObject->AsString(); // TODO: see if StaticDataObject can be replaced by something based on // Constant to avoid dealing with endianness here for KIND>1. // This will also avoid making string copies here. if (!string) TODO(loc, "StaticDataObject::Pointer substring with kind > 1"); fir::ExtendedValue exv = fir::factory::createStringLiteral(builder, getLoc(), *string); auto flags = fir::FortranVariableFlagsAttr::get( builder.getContext(), fir::FortranVariableFlagsEnum::parameter); partInfo.base = hlfir::genDeclare(loc, builder, exv, ".stringlit", flags); partInfo.typeParams.push_back(fir::getLen(exv)); return partInfo.base.getElementOrSequenceType(); } mlir::Type visit(const Fortran::evaluate::SymbolRef &symbolRef, PartInfo &partInfo) { partInfo.base = gen(symbolRef); hlfir::genLengthParameters(getLoc(), getBuilder(), partInfo.base, partInfo.typeParams); return partInfo.base.getElementOrSequenceType(); } mlir::Type visit(const Fortran::evaluate::ArrayRef &arrayRef, PartInfo &partInfo) { mlir::Type baseType; if (const auto *component = arrayRef.base().UnwrapComponent()) baseType = visit(*component, partInfo); baseType = visit(arrayRef.base().GetLastSymbol(), partInfo); fir::FirOpBuilder &builder = getBuilder(); mlir::Location loc = getLoc(); mlir::Type idxTy = builder.getIndexType(); llvm::SmallVector> bounds; auto getBounds = [&](unsigned i) { if (bounds.empty()) bounds = hlfir::genBounds(loc, builder, partInfo.base); return bounds[i]; }; auto frontEndResultShape = Fortran::evaluate::GetShape(converter.getFoldingContext(), arrayRef); llvm::SmallVector resultExtents; fir::SequenceType::Shape resultTypeShape; for (auto subscript : llvm::enumerate(arrayRef.subscript())) { if (const auto *triplet = std::get_if(&subscript.value().u)) { mlir::Value lb, ub; if (const auto &lbExpr = triplet->lower()) lb = genSubscript(*lbExpr); else lb = getBounds(subscript.index()).first; if (const auto &ubExpr = triplet->upper()) ub = genSubscript(*ubExpr); else ub = getBounds(subscript.index()).second; lb = builder.createConvert(loc, idxTy, lb); ub = builder.createConvert(loc, idxTy, ub); mlir::Value stride = genSubscript(triplet->stride()); stride = builder.createConvert(loc, idxTy, stride); mlir::Value extent; // Use constant extent if possible. The main advantage to do this now // is to get the best FIR array types as possible while lowering. if (frontEndResultShape) if (auto maybeI64 = Fortran::evaluate::ToInt64( frontEndResultShape->at(resultExtents.size()))) { resultTypeShape.push_back(*maybeI64); extent = builder.createIntegerConstant(loc, idxTy, *maybeI64); } if (!extent) { extent = builder.genExtentFromTriplet(loc, lb, ub, stride, idxTy); resultTypeShape.push_back(fir::SequenceType::getUnknownExtent()); } partInfo.subscripts.emplace_back( hlfir::DesignateOp::Triplet{lb, ub, stride}); resultExtents.push_back(extent); } else { const auto &expr = std::get( subscript.value().u) .value(); if (expr.Rank() > 0) TODO(getLoc(), "vector subscripts in HLFIR"); partInfo.subscripts.push_back(genSubscript(expr)); } } assert(resultExtents.size() == resultTypeShape.size() && "inconsistent hlfir.designate shape"); mlir::Type resultType = baseType.cast().getEleTy(); if (!resultTypeShape.empty()) { resultType = fir::SequenceType::get(resultTypeShape, resultType); partInfo.resultShape = builder.genShape(loc, resultExtents); } return resultType; } mlir::Type visit(const Fortran::evaluate::Component &component, PartInfo &partInfo) { TODO(getLoc(), "lowering component to HLFIR"); } /// Lower a subscript expression. If it is a scalar subscript that is /// a variable, it is loaded into an integer value. template hlfir::EntityWithAttributes genSubscript(const Fortran::evaluate::Expr &expr); mlir::Location getLoc() const { return loc; } Fortran::lower::AbstractConverter &getConverter() { return converter; } fir::FirOpBuilder &getBuilder() { return converter.getFirOpBuilder(); } Fortran::lower::SymMap &getSymMap() { return symMap; } Fortran::lower::StatementContext &getStmtCtx() { return stmtCtx; } Fortran::lower::AbstractConverter &converter; Fortran::lower::SymMap &symMap; Fortran::lower::StatementContext &stmtCtx; mlir::Location loc; }; //===--------------------------------------------------------------------===// // Binary Operation implementation //===--------------------------------------------------------------------===// template struct BinaryOp {}; #undef GENBIN #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp) \ template \ struct BinaryOp>> { \ using Op = Fortran::evaluate::GenBinEvOp>; \ static hlfir::EntityWithAttributes gen(mlir::Location loc, \ fir::FirOpBuilder &builder, \ const Op &, hlfir::Entity lhs, \ hlfir::Entity rhs) { \ return hlfir::EntityWithAttributes{ \ builder.create(loc, lhs, rhs)}; \ } \ }; GENBIN(Add, Integer, mlir::arith::AddIOp) GENBIN(Add, Real, mlir::arith::AddFOp) GENBIN(Add, Complex, fir::AddcOp) GENBIN(Subtract, Integer, mlir::arith::SubIOp) GENBIN(Subtract, Real, mlir::arith::SubFOp) GENBIN(Subtract, Complex, fir::SubcOp) GENBIN(Multiply, Integer, mlir::arith::MulIOp) GENBIN(Multiply, Real, mlir::arith::MulFOp) GENBIN(Multiply, Complex, fir::MulcOp) GENBIN(Divide, Integer, mlir::arith::DivSIOp) GENBIN(Divide, Real, mlir::arith::DivFOp) GENBIN(Divide, Complex, fir::DivcOp) template struct BinaryOp>> { using Op = Fortran::evaluate::Power>; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &, hlfir::Entity lhs, hlfir::Entity rhs) { mlir::Type ty = Fortran::lower::getFIRType(builder.getContext(), TC, KIND, /*params=*/std::nullopt); return hlfir::EntityWithAttributes{ Fortran::lower::genPow(builder, loc, ty, lhs, rhs)}; } }; template struct BinaryOp< Fortran::evaluate::RealToIntPower>> { using Op = Fortran::evaluate::RealToIntPower>; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &, hlfir::Entity lhs, hlfir::Entity rhs) { mlir::Type ty = Fortran::lower::getFIRType(builder.getContext(), TC, KIND, /*params=*/std::nullopt); return hlfir::EntityWithAttributes{ Fortran::lower::genPow(builder, loc, ty, lhs, rhs)}; } }; template struct BinaryOp< Fortran::evaluate::Extremum>> { using Op = Fortran::evaluate::Extremum>; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &op, hlfir::Entity lhs, hlfir::Entity rhs) { llvm::SmallVector args{lhs, rhs}; fir::ExtendedValue res = op.ordering == Fortran::evaluate::Ordering::Greater ? Fortran::lower::genMax(builder, loc, args) : Fortran::lower::genMin(builder, loc, args); return hlfir::EntityWithAttributes{fir::getBase(res)}; } }; // evaluate::Extremum is only created by the front-end when building compiler // generated expressions (like when folding LEN() or shape/bounds inquiries). // MIN and MAX are represented as evaluate::ProcedureRef and are not going // through here. So far the frontend does not generate character Extremum so // there is no way to test it. template struct BinaryOp>> { using Op = Fortran::evaluate::Extremum< Fortran::evaluate::Type>; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &, const Op &, hlfir::Entity, hlfir::Entity) { fir::emitFatalError(loc, "Fortran::evaluate::Extremum are unexpected"); } static void genResultTypeParams(mlir::Location loc, fir::FirOpBuilder &, hlfir::Entity, hlfir::Entity, llvm::SmallVectorImpl &) { fir::emitFatalError(loc, "Fortran::evaluate::Extremum are unexpected"); } }; /// Convert parser's INTEGER relational operators to MLIR. static mlir::arith::CmpIPredicate translateRelational(Fortran::common::RelationalOperator rop) { switch (rop) { case Fortran::common::RelationalOperator::LT: return mlir::arith::CmpIPredicate::slt; case Fortran::common::RelationalOperator::LE: return mlir::arith::CmpIPredicate::sle; case Fortran::common::RelationalOperator::EQ: return mlir::arith::CmpIPredicate::eq; case Fortran::common::RelationalOperator::NE: return mlir::arith::CmpIPredicate::ne; case Fortran::common::RelationalOperator::GT: return mlir::arith::CmpIPredicate::sgt; case Fortran::common::RelationalOperator::GE: return mlir::arith::CmpIPredicate::sge; } llvm_unreachable("unhandled INTEGER relational operator"); } /// 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 mlir::arith::CmpFPredicate translateFloatRelational(Fortran::common::RelationalOperator rop) { switch (rop) { case Fortran::common::RelationalOperator::LT: return mlir::arith::CmpFPredicate::OLT; case Fortran::common::RelationalOperator::LE: return mlir::arith::CmpFPredicate::OLE; case Fortran::common::RelationalOperator::EQ: return mlir::arith::CmpFPredicate::OEQ; case Fortran::common::RelationalOperator::NE: return mlir::arith::CmpFPredicate::UNE; case Fortran::common::RelationalOperator::GT: return mlir::arith::CmpFPredicate::OGT; case Fortran::common::RelationalOperator::GE: return mlir::arith::CmpFPredicate::OGE; } llvm_unreachable("unhandled REAL relational operator"); } template struct BinaryOp>> { using Op = Fortran::evaluate::Relational< Fortran::evaluate::Type>; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &op, hlfir::Entity lhs, hlfir::Entity rhs) { auto cmp = builder.create( loc, translateRelational(op.opr), lhs, rhs); return hlfir::EntityWithAttributes{cmp}; } }; template struct BinaryOp>> { using Op = Fortran::evaluate::Relational< Fortran::evaluate::Type>; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &op, hlfir::Entity lhs, hlfir::Entity rhs) { auto cmp = builder.create( loc, translateFloatRelational(op.opr), lhs, rhs); return hlfir::EntityWithAttributes{cmp}; } }; template struct BinaryOp>> { using Op = Fortran::evaluate::Relational< Fortran::evaluate::Type>; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &op, hlfir::Entity lhs, hlfir::Entity rhs) { auto cmp = builder.create( loc, translateFloatRelational(op.opr), lhs, rhs); return hlfir::EntityWithAttributes{cmp}; } }; template struct BinaryOp>> { using Op = Fortran::evaluate::Relational< Fortran::evaluate::Type>; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &op, hlfir::Entity lhs, hlfir::Entity rhs) { auto [lhsExv, lhsCleanUp] = hlfir::translateToExtendedValue(loc, builder, lhs); auto [rhsExv, rhsCleanUp] = hlfir::translateToExtendedValue(loc, builder, rhs); auto cmp = fir::runtime::genCharCompare( builder, loc, translateRelational(op.opr), lhsExv, rhsExv); if (lhsCleanUp) (*lhsCleanUp)(); if (rhsCleanUp) (*rhsCleanUp)(); return hlfir::EntityWithAttributes{cmp}; } }; template struct BinaryOp> { using Op = Fortran::evaluate::LogicalOperation; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &op, hlfir::Entity lhs, hlfir::Entity rhs) { mlir::Type i1Type = builder.getI1Type(); mlir::Value i1Lhs = builder.createConvert(loc, i1Type, lhs); mlir::Value i1Rhs = builder.createConvert(loc, i1Type, rhs); switch (op.logicalOperator) { case Fortran::evaluate::LogicalOperator::And: return hlfir::EntityWithAttributes{ builder.create(loc, i1Lhs, i1Rhs)}; case Fortran::evaluate::LogicalOperator::Or: return hlfir::EntityWithAttributes{ builder.create(loc, i1Lhs, i1Rhs)}; case Fortran::evaluate::LogicalOperator::Eqv: return hlfir::EntityWithAttributes{builder.create( loc, mlir::arith::CmpIPredicate::eq, i1Lhs, i1Rhs)}; case Fortran::evaluate::LogicalOperator::Neqv: return hlfir::EntityWithAttributes{builder.create( loc, mlir::arith::CmpIPredicate::ne, i1Lhs, i1Rhs)}; case Fortran::evaluate::LogicalOperator::Not: // lib/evaluate expression for .NOT. is Fortran::evaluate::Not. llvm_unreachable(".NOT. is not a binary operator"); } llvm_unreachable("unhandled logical operation"); } }; template struct BinaryOp> { using Op = Fortran::evaluate::ComplexConstructor; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &, hlfir::Entity lhs, hlfir::Entity rhs) { mlir::Value res = fir::factory::Complex{builder, loc}.createComplex(KIND, lhs, rhs); return hlfir::EntityWithAttributes{res}; } }; template struct BinaryOp> { using Op = Fortran::evaluate::SetLength; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &, hlfir::Entity string, hlfir::Entity length) { return hlfir::EntityWithAttributes{ builder.create(loc, string, length)}; } static void genResultTypeParams(mlir::Location, fir::FirOpBuilder &, hlfir::Entity, hlfir::Entity rhs, llvm::SmallVectorImpl &resultTypeParams) { resultTypeParams.push_back(rhs); } }; template struct BinaryOp> { using Op = Fortran::evaluate::Concat; hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &, hlfir::Entity lhs, hlfir::Entity rhs) { assert(len && "genResultTypeParams must have been called"); auto concat = builder.create(loc, mlir::ValueRange{lhs, rhs}, len); return hlfir::EntityWithAttributes{concat.getResult()}; } void genResultTypeParams(mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity lhs, hlfir::Entity rhs, llvm::SmallVectorImpl &resultTypeParams) { llvm::SmallVector lengths; hlfir::genLengthParameters(loc, builder, lhs, lengths); hlfir::genLengthParameters(loc, builder, rhs, lengths); assert(lengths.size() == 2 && "lacks rhs or lhs length"); mlir::Type idxType = builder.getIndexType(); mlir::Value lhsLen = builder.createConvert(loc, idxType, lengths[0]); mlir::Value rhsLen = builder.createConvert(loc, idxType, lengths[1]); len = builder.create(loc, lhsLen, rhsLen); resultTypeParams.push_back(len); } private: mlir::Value len{}; }; //===--------------------------------------------------------------------===// // Unary Operation implementation //===--------------------------------------------------------------------===// template struct UnaryOp {}; template struct UnaryOp> { using Op = Fortran::evaluate::Not; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &, hlfir::Entity lhs) { mlir::Value one = builder.createBool(loc, true); mlir::Value val = builder.createConvert(loc, builder.getI1Type(), lhs); return hlfir::EntityWithAttributes{ builder.create(loc, val, one)}; } }; template struct UnaryOp>> { using Op = Fortran::evaluate::Negate< Fortran::evaluate::Type>; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &, hlfir::Entity lhs) { // Like LLVM, integer negation is the binary op "0 - value" mlir::Type type = Fortran::lower::getFIRType( builder.getContext(), Fortran::common::TypeCategory::Integer, KIND, /*params=*/std::nullopt); mlir::Value zero = builder.createIntegerConstant(loc, type, 0); return hlfir::EntityWithAttributes{ builder.create(loc, zero, lhs)}; } }; template struct UnaryOp>> { using Op = Fortran::evaluate::Negate< Fortran::evaluate::Type>; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &, hlfir::Entity lhs) { return hlfir::EntityWithAttributes{ builder.create(loc, lhs)}; } }; template struct UnaryOp>> { using Op = Fortran::evaluate::Negate< Fortran::evaluate::Type>; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &, hlfir::Entity lhs) { return hlfir::EntityWithAttributes{builder.create(loc, lhs)}; } }; template struct UnaryOp> { using Op = Fortran::evaluate::ComplexComponent; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &op, hlfir::Entity lhs) { mlir::Value res = fir::factory::Complex{builder, loc}.extractComplexPart( lhs, op.isImaginaryPart); return hlfir::EntityWithAttributes{res}; } }; template struct UnaryOp> { using Op = Fortran::evaluate::Parentheses; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &op, hlfir::Entity lhs) { if (lhs.isVariable()) return hlfir::EntityWithAttributes{ builder.create(loc, lhs)}; return hlfir::EntityWithAttributes{ builder.create(loc, lhs.getType(), lhs)}; } static void genResultTypeParams(mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity lhs, llvm::SmallVectorImpl &resultTypeParams) { hlfir::genLengthParameters(loc, builder, lhs, resultTypeParams); } }; template struct UnaryOp< Fortran::evaluate::Convert, TC2>> { using Op = Fortran::evaluate::Convert, TC2>; static hlfir::EntityWithAttributes gen(mlir::Location loc, fir::FirOpBuilder &builder, const Op &, hlfir::Entity lhs) { if constexpr (TC1 == Fortran::common::TypeCategory::Character && TC2 == TC1) { TODO(loc, "character conversion in HLFIR"); } mlir::Type type = Fortran::lower::getFIRType(builder.getContext(), TC1, KIND, /*params=*/std::nullopt); mlir::Value res = builder.convertWithSemantics(loc, type, lhs); return hlfir::EntityWithAttributes{res}; } static void genResultTypeParams(mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity lhs, llvm::SmallVectorImpl &resultTypeParams) { hlfir::genLengthParameters(loc, builder, lhs, resultTypeParams); } }; /// Lower Expr to HLFIR. class HlfirBuilder { public: HlfirBuilder(mlir::Location loc, Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) : converter{converter}, symMap{symMap}, stmtCtx{stmtCtx}, loc{loc} {} template hlfir::EntityWithAttributes gen(const Fortran::evaluate::Expr &expr) { return std::visit([&](const auto &x) { return gen(x); }, expr.u); } private: hlfir::EntityWithAttributes gen(const Fortran::evaluate::BOZLiteralConstant &expr) { fir::emitFatalError(loc, "BOZ literal must be replaced by semantics"); } hlfir::EntityWithAttributes gen(const Fortran::evaluate::NullPointer &expr) { TODO(getLoc(), "lowering NullPointer to HLFIR"); } hlfir::EntityWithAttributes gen(const Fortran::evaluate::ProcedureDesignator &expr) { TODO(getLoc(), "lowering ProcDes to HLFIR"); } hlfir::EntityWithAttributes gen(const Fortran::evaluate::ProcedureRef &expr) { TODO(getLoc(), "lowering ProcRef to HLFIR"); } template hlfir::EntityWithAttributes gen(const Fortran::evaluate::Designator &designator) { return HlfirDesignatorBuilder(getLoc(), getConverter(), getSymMap(), getStmtCtx()) .gen(designator.u); } template hlfir::EntityWithAttributes gen(const Fortran::evaluate::FunctionRef &expr) { mlir::Type resType = Fortran::lower::TypeBuilder::genType(getConverter(), expr); auto result = Fortran::lower::convertCallToHLFIR( getLoc(), getConverter(), expr, resType, getSymMap(), getStmtCtx()); assert(result.has_value()); return *result; } template hlfir::EntityWithAttributes gen(const Fortran::evaluate::Constant &expr) { mlir::Location loc = getLoc(); fir::FirOpBuilder &builder = getBuilder(); fir::ExtendedValue exv = Fortran::lower::convertConstant( converter, loc, expr, /*outlineBigConstantInReadOnlyMemory=*/true); if (const auto *scalarBox = exv.getUnboxed()) if (fir::isa_trivial(scalarBox->getType())) return hlfir::EntityWithAttributes(*scalarBox); if (auto addressOf = fir::getBase(exv).getDefiningOp()) { auto flags = fir::FortranVariableFlagsAttr::get( builder.getContext(), fir::FortranVariableFlagsEnum::parameter); return hlfir::genDeclare( loc, builder, exv, addressOf.getSymbol().getRootReference().getValue(), flags); } fir::emitFatalError(loc, "Constant was lowered to unexpected format"); } template hlfir::EntityWithAttributes gen(const Fortran::evaluate::ArrayConstructor &expr) { TODO(getLoc(), "lowering ArrayCtor to HLFIR"); } template hlfir::EntityWithAttributes gen(const Fortran::evaluate::Operation &op) { auto &builder = getBuilder(); mlir::Location loc = getLoc(); const int rank = op.Rank(); UnaryOp unaryOp; auto left = hlfir::loadTrivialScalar(loc, builder, gen(op.left())); llvm::SmallVector typeParams; if constexpr (R::category == Fortran::common::TypeCategory::Character) { unaryOp.genResultTypeParams(loc, builder, left, typeParams); } if (rank == 0) return unaryOp.gen(loc, builder, op.derived(), left); // Elemental expression. mlir::Type elementType; if constexpr (R::category == Fortran::common::TypeCategory::Derived) { elementType = Fortran::lower::translateDerivedTypeToFIRType( getConverter(), op.derived().GetType().GetDerivedTypeSpec()); } else { elementType = Fortran::lower::getFIRType(builder.getContext(), R::category, R::kind, /*params=*/std::nullopt); } mlir::Value shape = hlfir::genShape(loc, builder, left); auto genKernel = [&op, &left, &unaryOp]( mlir::Location l, fir::FirOpBuilder &b, mlir::ValueRange oneBasedIndices) -> hlfir::Entity { auto leftElement = hlfir::getElementAt(l, b, left, oneBasedIndices); auto leftVal = hlfir::loadTrivialScalar(l, b, leftElement); return unaryOp.gen(l, b, op.derived(), leftVal); }; // TODO: deal with hlfir.elemental result destruction. return hlfir::EntityWithAttributes{hlfir::genElementalOp( loc, builder, elementType, shape, typeParams, genKernel)}; } template hlfir::EntityWithAttributes gen(const Fortran::evaluate::Operation &op) { auto &builder = getBuilder(); mlir::Location loc = getLoc(); const int rank = op.Rank(); BinaryOp binaryOp; auto left = hlfir::loadTrivialScalar(loc, builder, gen(op.left())); auto right = hlfir::loadTrivialScalar(loc, builder, gen(op.right())); llvm::SmallVector typeParams; if constexpr (R::category == Fortran::common::TypeCategory::Character) { binaryOp.genResultTypeParams(loc, builder, left, right, typeParams); } if (rank == 0) return binaryOp.gen(loc, builder, op.derived(), left, right); // Elemental expression. mlir::Type elementType = Fortran::lower::getFIRType(builder.getContext(), R::category, R::kind, /*params=*/std::nullopt); // TODO: "merge" shape, get cst shape from front-end if possible. mlir::Value shape; if (left.isArray()) { shape = hlfir::genShape(loc, builder, left); } else { assert(right.isArray() && "must have at least one array operand"); shape = hlfir::genShape(loc, builder, right); } auto genKernel = [&op, &left, &right, &binaryOp]( mlir::Location l, fir::FirOpBuilder &b, mlir::ValueRange oneBasedIndices) -> hlfir::Entity { auto leftElement = hlfir::getElementAt(l, b, left, oneBasedIndices); auto rightElement = hlfir::getElementAt(l, b, right, oneBasedIndices); auto leftVal = hlfir::loadTrivialScalar(l, b, leftElement); auto rightVal = hlfir::loadTrivialScalar(l, b, rightElement); return binaryOp.gen(l, b, op.derived(), leftVal, rightVal); }; // TODO: deal with hlfir.elemental result destruction. return hlfir::EntityWithAttributes{hlfir::genElementalOp( loc, builder, elementType, shape, typeParams, genKernel)}; } hlfir::EntityWithAttributes gen(const Fortran::evaluate::Relational &op) { return std::visit([&](const auto &x) { return gen(x); }, op.u); } hlfir::EntityWithAttributes gen(const Fortran::evaluate::TypeParamInquiry &) { TODO(getLoc(), "lowering type parameter inquiry to HLFIR"); } hlfir::EntityWithAttributes gen(const Fortran::evaluate::DescriptorInquiry &desc) { mlir::Location loc = getLoc(); auto &builder = getBuilder(); hlfir::EntityWithAttributes entity = HlfirDesignatorBuilder(getLoc(), getConverter(), getSymMap(), getStmtCtx()) .gen(desc.base()); using ResTy = Fortran::evaluate::DescriptorInquiry::Result; mlir::Type resultType = getConverter().genType(ResTy::category, ResTy::kind); auto castResult = [&](mlir::Value v) { return hlfir::EntityWithAttributes{ builder.createConvert(loc, resultType, v)}; }; switch (desc.field()) { case Fortran::evaluate::DescriptorInquiry::Field::Len: return castResult(hlfir::genCharLength(loc, builder, entity)); case Fortran::evaluate::DescriptorInquiry::Field::LowerBound: TODO(loc, "lower bound inquiry in HLFIR"); case Fortran::evaluate::DescriptorInquiry::Field::Extent: TODO(loc, "extent inquiry in HLFIR"); case Fortran::evaluate::DescriptorInquiry::Field::Rank: TODO(loc, "rank inquiry on assumed rank"); case Fortran::evaluate::DescriptorInquiry::Field::Stride: // So far the front end does not generate this inquiry. TODO(loc, "stride inquiry"); } llvm_unreachable("unknown descriptor inquiry"); } hlfir::EntityWithAttributes gen(const Fortran::evaluate::ImpliedDoIndex &var) { TODO(getLoc(), "lowering implied do index to HLFIR"); } hlfir::EntityWithAttributes gen(const Fortran::evaluate::StructureConstructor &var) { TODO(getLoc(), "lowering structure constructor to HLFIR"); } mlir::Location getLoc() const { return loc; } Fortran::lower::AbstractConverter &getConverter() { return converter; } fir::FirOpBuilder &getBuilder() { return converter.getFirOpBuilder(); } Fortran::lower::SymMap &getSymMap() { return symMap; } Fortran::lower::StatementContext &getStmtCtx() { return stmtCtx; } Fortran::lower::AbstractConverter &converter; Fortran::lower::SymMap &symMap; Fortran::lower::StatementContext &stmtCtx; mlir::Location loc; }; template hlfir::EntityWithAttributes HlfirDesignatorBuilder::genSubscript(const Fortran::evaluate::Expr &expr) { auto loweredExpr = HlfirBuilder(getLoc(), getConverter(), getSymMap(), getStmtCtx()) .gen(expr); if (!loweredExpr.isArray()) { fir::FirOpBuilder &builder = getBuilder(); if (loweredExpr.isVariable()) return hlfir::EntityWithAttributes{ hlfir::loadTrivialScalar(loc, builder, loweredExpr).getBase()}; // Skip constant conversions that litters designators and makes generated // IR harder to read: directly use index constants for constant subscripts. mlir::Type idxTy = builder.getIndexType(); if (loweredExpr.getType() != idxTy) if (auto cstIndex = fir::getIntIfConstant(loweredExpr)) return hlfir::EntityWithAttributes{ builder.createIntegerConstant(getLoc(), idxTy, *cstIndex)}; } return loweredExpr; } } // namespace hlfir::EntityWithAttributes Fortran::lower::convertExprToHLFIR( mlir::Location loc, Fortran::lower::AbstractConverter &converter, const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { return HlfirBuilder(loc, converter, symMap, stmtCtx).gen(expr); }