431 lines · cpp
1//===-- VectorSubscripts.cpp -- Vector subscripts tools -------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/10//11//===----------------------------------------------------------------------===//12 13#include "flang/Lower/VectorSubscripts.h"14#include "flang/Lower/AbstractConverter.h"15#include "flang/Lower/Support/Utils.h"16#include "flang/Optimizer/Builder/Character.h"17#include "flang/Optimizer/Builder/Complex.h"18#include "flang/Optimizer/Builder/FIRBuilder.h"19#include "flang/Optimizer/Builder/Todo.h"20#include "flang/Semantics/expression.h"21 22namespace {23/// Helper class to lower a designator containing vector subscripts into a24/// lowered representation that can be worked with.25class VectorSubscriptBoxBuilder {26public:27 VectorSubscriptBoxBuilder(mlir::Location loc,28 Fortran::lower::AbstractConverter &converter,29 Fortran::lower::StatementContext &stmtCtx)30 : converter{converter}, stmtCtx{stmtCtx}, loc{loc} {}31 32 Fortran::lower::VectorSubscriptBox gen(const Fortran::lower::SomeExpr &expr) {33 elementType = genDesignator(expr);34 return Fortran::lower::VectorSubscriptBox(35 std::move(loweredBase), std::move(loweredSubscripts),36 std::move(componentPath), substringBounds, elementType);37 }38 39private:40 using LoweredVectorSubscript =41 Fortran::lower::VectorSubscriptBox::LoweredVectorSubscript;42 using LoweredTriplet = Fortran::lower::VectorSubscriptBox::LoweredTriplet;43 using LoweredSubscript = Fortran::lower::VectorSubscriptBox::LoweredSubscript;44 using MaybeSubstring = Fortran::lower::VectorSubscriptBox::MaybeSubstring;45 46 /// genDesignator unwraps a Designator<T> and calls `gen` on what the47 /// designator actually contains.48 template <typename A>49 mlir::Type genDesignator(const A &) {50 fir::emitFatalError(loc, "expr must contain a designator");51 }52 template <typename T>53 mlir::Type genDesignator(const Fortran::evaluate::Expr<T> &expr) {54 using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u);55 using Designator = Fortran::evaluate::Designator<T>;56 if constexpr (Fortran::common::HasMember<Designator, ExprVariant>) {57 const auto &designator = std::get<Designator>(expr.u);58 return Fortran::common::visit([&](const auto &x) { return gen(x); },59 designator.u);60 } else {61 return Fortran::common::visit(62 [&](const auto &x) { return genDesignator(x); }, expr.u);63 }64 }65 66 // The gen(X) methods visit X to lower its base and subscripts and return the67 // type of X elements.68 69 mlir::Type gen(const Fortran::evaluate::DataRef &dataRef) {70 return Fortran::common::visit(71 [&](const auto &ref) -> mlir::Type { return gen(ref); }, dataRef.u);72 }73 74 mlir::Type gen(const Fortran::evaluate::SymbolRef &symRef) {75 // Never visited because expr lowering is used to lowered the ranked76 // ArrayRef.77 fir::emitFatalError(78 loc, "expected at least one ArrayRef with vector susbcripts");79 }80 81 mlir::Type gen(const Fortran::evaluate::Substring &substring) {82 // StaticDataObject::Pointer bases are constants and cannot be83 // subscripted, so the base must be a DataRef here.84 mlir::Type baseElementType =85 gen(std::get<Fortran::evaluate::DataRef>(substring.parent()));86 fir::FirOpBuilder &builder = converter.getFirOpBuilder();87 mlir::Type idxTy = builder.getIndexType();88 mlir::Value lb = genScalarValue(substring.lower());89 substringBounds.emplace_back(builder.createConvert(loc, idxTy, lb));90 if (const auto &ubExpr = substring.upper()) {91 mlir::Value ub = genScalarValue(*ubExpr);92 substringBounds.emplace_back(builder.createConvert(loc, idxTy, ub));93 }94 return baseElementType;95 }96 97 mlir::Type gen(const Fortran::evaluate::ComplexPart &complexPart) {98 auto complexType = gen(complexPart.complex());99 fir::FirOpBuilder &builder = converter.getFirOpBuilder();100 mlir::Type i32Ty = builder.getI32Type(); // llvm's GEP requires i32101 mlir::Value offset = builder.createIntegerConstant(102 loc, i32Ty,103 complexPart.part() == Fortran::evaluate::ComplexPart::Part::RE ? 0 : 1);104 componentPath.emplace_back(offset);105 return fir::factory::Complex{builder, loc}.getComplexPartType(complexType);106 }107 108 mlir::Type gen(const Fortran::evaluate::Component &component) {109 auto recTy = mlir::cast<fir::RecordType>(gen(component.base()));110 const Fortran::semantics::Symbol &componentSymbol =111 component.GetLastSymbol();112 // Parent components will not be found here, they are not part113 // of the FIR type and cannot be used in the path yet.114 if (componentSymbol.test(Fortran::semantics::Symbol::Flag::ParentComp))115 TODO(loc, "reference to parent component");116 mlir::Type fldTy = fir::FieldType::get(&converter.getMLIRContext());117 llvm::StringRef componentName = toStringRef(componentSymbol.name());118 // Parameters threading in field_index is not yet very clear. We only119 // have the ones of the ranked array ref at hand, but it looks like120 // the fir.field_index expects the one of the direct base.121 if (recTy.getNumLenParams() != 0)122 TODO(loc, "threading length parameters in field index op");123 fir::FirOpBuilder &builder = converter.getFirOpBuilder();124 componentPath.emplace_back(125 fir::FieldIndexOp::create(builder, loc, fldTy, componentName, recTy,126 /*typeParams=*/mlir::ValueRange{}));127 return fir::unwrapSequenceType(recTy.getType(componentName));128 }129 130 mlir::Type gen(const Fortran::evaluate::ArrayRef &arrayRef) {131 auto isTripletOrVector =132 [](const Fortran::evaluate::Subscript &subscript) -> bool {133 return Fortran::common::visit(134 Fortran::common::visitors{135 [](const Fortran::evaluate::IndirectSubscriptIntegerExpr &expr) {136 return expr.value().Rank() != 0;137 },138 [&](const Fortran::evaluate::Triplet &) { return true; }},139 subscript.u);140 };141 if (llvm::any_of(arrayRef.subscript(), isTripletOrVector))142 return genRankedArrayRefSubscriptAndBase(arrayRef);143 144 // This is a scalar ArrayRef (only scalar indexes), collect the indexes and145 // visit the base that must contain another arrayRef with the vector146 // subscript.147 mlir::Type elementType = gen(namedEntityToDataRef(arrayRef.base()));148 for (const Fortran::evaluate::Subscript &subscript : arrayRef.subscript()) {149 const auto &expr =150 std::get<Fortran::evaluate::IndirectSubscriptIntegerExpr>(151 subscript.u);152 componentPath.emplace_back(genScalarValue(expr.value()));153 }154 return elementType;155 }156 157 /// Lower the subscripts and base of the ArrayRef that is an array (there must158 /// be one since there is a vector subscript, and there can only be one159 /// according to C925).160 mlir::Type genRankedArrayRefSubscriptAndBase(161 const Fortran::evaluate::ArrayRef &arrayRef) {162 // Lower the save the base163 Fortran::lower::SomeExpr baseExpr = namedEntityToExpr(arrayRef.base());164 loweredBase = converter.genExprAddr(baseExpr, stmtCtx);165 // Lower and save the subscripts166 fir::FirOpBuilder &builder = converter.getFirOpBuilder();167 mlir::Type idxTy = builder.getIndexType();168 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);169 for (const auto &subscript : llvm::enumerate(arrayRef.subscript())) {170 Fortran::common::visit(171 Fortran::common::visitors{172 [&](const Fortran::evaluate::IndirectSubscriptIntegerExpr &expr) {173 if (expr.value().Rank() == 0) {174 // Simple scalar subscript175 loweredSubscripts.emplace_back(genScalarValue(expr.value()));176 } else {177 // Vector subscript.178 // Remove conversion if any to avoid temp creation that may179 // have been added by the front-end to avoid the creation of a180 // temp array value.181 auto vector = converter.genExprAddr(182 ignoreEvConvert(expr.value()), stmtCtx);183 mlir::Value size =184 fir::factory::readExtent(builder, loc, vector, /*dim=*/0);185 size = builder.createConvert(loc, idxTy, size);186 loweredSubscripts.emplace_back(187 LoweredVectorSubscript{std::move(vector), size});188 }189 },190 [&](const Fortran::evaluate::Triplet &triplet) {191 mlir::Value lb, ub;192 if (const auto &lbExpr = triplet.lower())193 lb = genScalarValue(*lbExpr);194 else195 lb = fir::factory::readLowerBound(builder, loc, loweredBase,196 subscript.index(), one);197 if (const auto &ubExpr = triplet.upper())198 ub = genScalarValue(*ubExpr);199 else200 ub = fir::factory::readExtent(builder, loc, loweredBase,201 subscript.index());202 lb = builder.createConvert(loc, idxTy, lb);203 ub = builder.createConvert(loc, idxTy, ub);204 mlir::Value stride = genScalarValue(triplet.stride());205 stride = builder.createConvert(loc, idxTy, stride);206 loweredSubscripts.emplace_back(LoweredTriplet{lb, ub, stride});207 },208 },209 subscript.value().u);210 }211 return fir::unwrapSequenceType(212 fir::unwrapPassByRefType(fir::getBase(loweredBase).getType()));213 }214 215 mlir::Type gen(const Fortran::evaluate::CoarrayRef &) {216 // Is this possible/legal ?217 TODO(loc, "coarray: reference to coarray object with vector subscript in "218 "IO input");219 }220 221 template <typename A>222 mlir::Value genScalarValue(const A &expr) {223 return fir::getBase(converter.genExprValue(toEvExpr(expr), stmtCtx));224 }225 226 Fortran::evaluate::DataRef227 namedEntityToDataRef(const Fortran::evaluate::NamedEntity &namedEntity) {228 if (namedEntity.IsSymbol())229 return Fortran::evaluate::DataRef{namedEntity.GetFirstSymbol()};230 return Fortran::evaluate::DataRef{namedEntity.GetComponent()};231 }232 233 Fortran::lower::SomeExpr234 namedEntityToExpr(const Fortran::evaluate::NamedEntity &namedEntity) {235 return Fortran::evaluate::AsGenericExpr(namedEntityToDataRef(namedEntity))236 .value();237 }238 239 Fortran::lower::AbstractConverter &converter;240 Fortran::lower::StatementContext &stmtCtx;241 mlir::Location loc;242 /// Elements of VectorSubscriptBox being built.243 fir::ExtendedValue loweredBase;244 llvm::SmallVector<LoweredSubscript, 16> loweredSubscripts;245 llvm::SmallVector<mlir::Value> componentPath;246 MaybeSubstring substringBounds;247 mlir::Type elementType;248};249} // namespace250 251Fortran::lower::VectorSubscriptBox Fortran::lower::genVectorSubscriptBox(252 mlir::Location loc, Fortran::lower::AbstractConverter &converter,253 Fortran::lower::StatementContext &stmtCtx,254 const Fortran::lower::SomeExpr &expr) {255 return VectorSubscriptBoxBuilder(loc, converter, stmtCtx).gen(expr);256}257 258template <typename LoopType, typename Generator>259mlir::Value Fortran::lower::VectorSubscriptBox::loopOverElementsBase(260 fir::FirOpBuilder &builder, mlir::Location loc,261 const Generator &elementalGenerator,262 [[maybe_unused]] mlir::Value initialCondition) {263 mlir::Value shape = builder.createShape(loc, loweredBase);264 mlir::Value slice = createSlice(builder, loc);265 266 // Create loop nest for triplets and vector subscripts in column267 // major order.268 llvm::SmallVector<mlir::Value> inductionVariables;269 LoopType outerLoop;270 for (auto [lb, ub, step] : genLoopBounds(builder, loc)) {271 LoopType loop;272 if constexpr (std::is_same_v<LoopType, fir::IterWhileOp>) {273 loop = fir::IterWhileOp::create(builder, loc, lb, ub, step,274 initialCondition);275 initialCondition = loop.getIterateVar();276 if (!outerLoop)277 outerLoop = loop;278 else279 fir::ResultOp::create(builder, loc, loop.getResult(0));280 } else {281 loop = fir::DoLoopOp::create(builder, loc, lb, ub, step,282 /*unordered=*/false);283 if (!outerLoop)284 outerLoop = loop;285 }286 builder.setInsertionPointToStart(loop.getBody());287 inductionVariables.push_back(loop.getInductionVar());288 }289 assert(outerLoop && !inductionVariables.empty() &&290 "at least one loop should be created");291 292 fir::ExtendedValue elem =293 getElementAt(builder, loc, shape, slice, inductionVariables);294 295 if constexpr (std::is_same_v<LoopType, fir::IterWhileOp>) {296 auto res = elementalGenerator(elem);297 fir::ResultOp::create(builder, loc, res);298 builder.setInsertionPointAfter(outerLoop);299 return outerLoop.getResult(0);300 } else {301 elementalGenerator(elem);302 builder.setInsertionPointAfter(outerLoop);303 return {};304 }305}306 307void Fortran::lower::VectorSubscriptBox::loopOverElements(308 fir::FirOpBuilder &builder, mlir::Location loc,309 const ElementalGenerator &elementalGenerator) {310 mlir::Value initialCondition;311 loopOverElementsBase<fir::DoLoopOp, ElementalGenerator>(312 builder, loc, elementalGenerator, initialCondition);313}314 315mlir::Value Fortran::lower::VectorSubscriptBox::loopOverElementsWhile(316 fir::FirOpBuilder &builder, mlir::Location loc,317 const ElementalGeneratorWithBoolReturn &elementalGenerator,318 mlir::Value initialCondition) {319 return loopOverElementsBase<fir::IterWhileOp,320 ElementalGeneratorWithBoolReturn>(321 builder, loc, elementalGenerator, initialCondition);322}323 324mlir::Value325Fortran::lower::VectorSubscriptBox::createSlice(fir::FirOpBuilder &builder,326 mlir::Location loc) {327 mlir::Type idxTy = builder.getIndexType();328 llvm::SmallVector<mlir::Value> triples;329 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);330 auto undef = fir::UndefOp::create(builder, loc, idxTy);331 for (const LoweredSubscript &subscript : loweredSubscripts)332 Fortran::common::visit(Fortran::common::visitors{333 [&](const LoweredTriplet &triplet) {334 triples.emplace_back(triplet.lb);335 triples.emplace_back(triplet.ub);336 triples.emplace_back(triplet.stride);337 },338 [&](const LoweredVectorSubscript &vector) {339 triples.emplace_back(one);340 triples.emplace_back(vector.size);341 triples.emplace_back(one);342 },343 [&](const mlir::Value &i) {344 triples.emplace_back(i);345 triples.emplace_back(undef);346 triples.emplace_back(undef);347 },348 },349 subscript);350 return fir::SliceOp::create(builder, loc, triples, componentPath);351}352 353llvm::SmallVector<std::tuple<mlir::Value, mlir::Value, mlir::Value>>354Fortran::lower::VectorSubscriptBox::genLoopBounds(fir::FirOpBuilder &builder,355 mlir::Location loc) {356 mlir::Type idxTy = builder.getIndexType();357 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);358 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);359 llvm::SmallVector<std::tuple<mlir::Value, mlir::Value, mlir::Value>> bounds;360 size_t dimension = loweredSubscripts.size();361 for (const LoweredSubscript &subscript : llvm::reverse(loweredSubscripts)) {362 --dimension;363 if (std::holds_alternative<mlir::Value>(subscript))364 continue;365 mlir::Value lb, ub, step;366 if (const auto *triplet = std::get_if<LoweredTriplet>(&subscript)) {367 mlir::Value extent = builder.genExtentFromTriplet(368 loc, triplet->lb, triplet->ub, triplet->stride, idxTy);369 mlir::Value baseLb = fir::factory::readLowerBound(370 builder, loc, loweredBase, dimension, one);371 baseLb = builder.createConvert(loc, idxTy, baseLb);372 lb = baseLb;373 ub = mlir::arith::SubIOp::create(builder, loc, idxTy, extent, one);374 ub = mlir::arith::AddIOp::create(builder, loc, idxTy, ub, baseLb);375 step = one;376 } else {377 const auto &vector = std::get<LoweredVectorSubscript>(subscript);378 lb = zero;379 ub = mlir::arith::SubIOp::create(builder, loc, idxTy, vector.size, one);380 step = one;381 }382 bounds.emplace_back(lb, ub, step);383 }384 return bounds;385}386 387fir::ExtendedValue Fortran::lower::VectorSubscriptBox::getElementAt(388 fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value shape,389 mlir::Value slice, mlir::ValueRange inductionVariables) {390 /// Generate the indexes for the array_coor inside the loops.391 mlir::Type idxTy = builder.getIndexType();392 llvm::SmallVector<mlir::Value> indexes;393 size_t inductionIdx = inductionVariables.size() - 1;394 for (const LoweredSubscript &subscript : loweredSubscripts)395 Fortran::common::visit(396 Fortran::common::visitors{397 [&](const LoweredTriplet &triplet) {398 indexes.emplace_back(inductionVariables[inductionIdx--]);399 },400 [&](const LoweredVectorSubscript &vector) {401 mlir::Value vecIndex = inductionVariables[inductionIdx--];402 mlir::Value vecBase = fir::getBase(vector.vector);403 mlir::Type vecEleTy = fir::unwrapSequenceType(404 fir::unwrapPassByRefType(vecBase.getType()));405 mlir::Type refTy = builder.getRefType(vecEleTy);406 auto vecEltRef = fir::CoordinateOp::create(builder, loc, refTy,407 vecBase, vecIndex);408 auto vecElt =409 fir::LoadOp::create(builder, loc, vecEleTy, vecEltRef);410 indexes.emplace_back(builder.createConvert(loc, idxTy, vecElt));411 },412 [&](const mlir::Value &i) {413 indexes.emplace_back(builder.createConvert(loc, idxTy, i));414 },415 },416 subscript);417 mlir::Type refTy = builder.getRefType(getElementType());418 auto elementAddr = fir::ArrayCoorOp::create(419 builder, loc, refTy, fir::getBase(loweredBase), shape, slice, indexes,420 fir::getTypeParams(loweredBase));421 fir::ExtendedValue element = fir::factory::arraySectionElementToExtendedValue(422 builder, loc, loweredBase, elementAddr, slice);423 if (!substringBounds.empty()) {424 const fir::CharBoxValue *charBox = element.getCharBox();425 assert(charBox && "substring requires CharBox base");426 fir::factory::CharacterExprHelper helper{builder, loc};427 return helper.createSubstring(*charBox, substringBounds);428 }429 return element;430}431