xref: /llvm-project/flang/include/flang/Optimizer/Builder/Factory.h (revision fac349a169976f822fb27f03e623fa0d28aec1f3)
1 //===-- Optimizer/Builder/Factory.h -----------------------------*- C++ -*-===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Templates to generate more complex code patterns in transformation passes.
10 // In transformation passes, front-end information such as is available in
11 // lowering is not available.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef FORTRAN_OPTIMIZER_BUILDER_FACTORY_H
16 #define FORTRAN_OPTIMIZER_BUILDER_FACTORY_H
17 
18 #include "flang/Optimizer/Dialect/FIROps.h"
19 #include "flang/Optimizer/Dialect/FIRType.h"
20 #include "mlir/Dialect/Func/IR/FuncOps.h"
21 #include "llvm/ADT/iterator_range.h"
22 
23 namespace mlir {
24 class Location;
25 class Value;
26 } // namespace mlir
27 
28 namespace fir::factory {
29 
attrFortranArrayOffsets()30 constexpr llvm::StringRef attrFortranArrayOffsets() {
31   return "Fortran.offsets";
32 }
33 
34 /// Generate a character copy with optimized forms.
35 ///
36 /// If the lengths are constant and equal, use load/store rather than a loop.
37 /// Otherwise, if the lengths are constant and the input is longer than the
38 /// output, generate a loop to move a truncated portion of the source to the
39 /// destination. Finally, if the lengths are runtime values or the destination
40 /// is longer than the source, move the entire source character and pad the
41 /// destination with spaces as needed.
42 template <typename B>
genCharacterCopy(mlir::Value src,mlir::Value srcLen,mlir::Value dst,mlir::Value dstLen,B & builder,mlir::Location loc)43 void genCharacterCopy(mlir::Value src, mlir::Value srcLen, mlir::Value dst,
44                       mlir::Value dstLen, B &builder, mlir::Location loc) {
45   auto srcTy =
46       mlir::cast<fir::CharacterType>(fir::dyn_cast_ptrEleTy(src.getType()));
47   auto dstTy =
48       mlir::cast<fir::CharacterType>(fir::dyn_cast_ptrEleTy(dst.getType()));
49   if (!srcLen && !dstLen && srcTy.getFKind() == dstTy.getFKind() &&
50       srcTy.getLen() == dstTy.getLen()) {
51     // same size, so just use load and store
52     auto load = builder.template create<fir::LoadOp>(loc, src);
53     builder.template create<fir::StoreOp>(loc, load, dst);
54     return;
55   }
56   auto zero = builder.template create<mlir::arith::ConstantIndexOp>(loc, 0);
57   auto one = builder.template create<mlir::arith::ConstantIndexOp>(loc, 1);
58   auto toArrayTy = [&](fir::CharacterType ty) {
59     return fir::ReferenceType::get(fir::SequenceType::get(
60         fir::SequenceType::ShapeRef{fir::SequenceType::getUnknownExtent()},
61         fir::CharacterType::getSingleton(ty.getContext(), ty.getFKind())));
62   };
63   auto toEleTy = [&](fir::ReferenceType ty) {
64     auto seqTy = mlir::cast<fir::SequenceType>(ty.getEleTy());
65     return mlir::cast<fir::CharacterType>(seqTy.getEleTy());
66   };
67   auto toCoorTy = [&](fir::ReferenceType ty) {
68     return fir::ReferenceType::get(toEleTy(ty));
69   };
70   if (!srcLen && !dstLen && srcTy.getLen() >= dstTy.getLen()) {
71     auto upper = builder.template create<mlir::arith::ConstantIndexOp>(
72         loc, dstTy.getLen() - 1);
73     auto loop = builder.template create<fir::DoLoopOp>(loc, zero, upper, one);
74     auto insPt = builder.saveInsertionPoint();
75     builder.setInsertionPointToStart(loop.getBody());
76     auto csrcTy = toArrayTy(srcTy);
77     auto csrc = builder.template create<fir::ConvertOp>(loc, csrcTy, src);
78     auto in = builder.template create<fir::CoordinateOp>(
79         loc, toCoorTy(csrcTy), csrc, loop.getInductionVar());
80     auto load = builder.template create<fir::LoadOp>(loc, in);
81     auto cdstTy = toArrayTy(dstTy);
82     auto cdst = builder.template create<fir::ConvertOp>(loc, cdstTy, dst);
83     auto out = builder.template create<fir::CoordinateOp>(
84         loc, toCoorTy(cdstTy), cdst, loop.getInductionVar());
85     mlir::Value cast =
86         srcTy.getFKind() == dstTy.getFKind()
87             ? load.getResult()
88             : builder
89                   .template create<fir::ConvertOp>(loc, toEleTy(cdstTy), load)
90                   .getResult();
91     builder.template create<fir::StoreOp>(loc, cast, out);
92     builder.restoreInsertionPoint(insPt);
93     return;
94   }
95   auto minusOne = [&](mlir::Value v) -> mlir::Value {
96     return builder.template create<mlir::arith::SubIOp>(
97         loc, builder.template create<fir::ConvertOp>(loc, one.getType(), v),
98         one);
99   };
100   mlir::Value len = dstLen ? minusOne(dstLen)
101                            : builder
102                                  .template create<mlir::arith::ConstantIndexOp>(
103                                      loc, dstTy.getLen() - 1)
104                                  .getResult();
105   auto loop = builder.template create<fir::DoLoopOp>(loc, zero, len, one);
106   auto insPt = builder.saveInsertionPoint();
107   builder.setInsertionPointToStart(loop.getBody());
108   mlir::Value slen =
109       srcLen
110           ? builder.template create<fir::ConvertOp>(loc, one.getType(), srcLen)
111                 .getResult()
112           : builder
113                 .template create<mlir::arith::ConstantIndexOp>(loc,
114                                                                srcTy.getLen())
115                 .getResult();
116   auto cond = builder.template create<mlir::arith::CmpIOp>(
117       loc, mlir::arith::CmpIPredicate::slt, loop.getInductionVar(), slen);
118   auto ifOp = builder.template create<fir::IfOp>(loc, cond, /*withElse=*/true);
119   builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
120   auto csrcTy = toArrayTy(srcTy);
121   auto csrc = builder.template create<fir::ConvertOp>(loc, csrcTy, src);
122   auto in = builder.template create<fir::CoordinateOp>(
123       loc, toCoorTy(csrcTy), csrc, loop.getInductionVar());
124   auto load = builder.template create<fir::LoadOp>(loc, in);
125   auto cdstTy = toArrayTy(dstTy);
126   auto cdst = builder.template create<fir::ConvertOp>(loc, cdstTy, dst);
127   auto out = builder.template create<fir::CoordinateOp>(
128       loc, toCoorTy(cdstTy), cdst, loop.getInductionVar());
129   mlir::Value cast =
130       srcTy.getFKind() == dstTy.getFKind()
131           ? load.getResult()
132           : builder.template create<fir::ConvertOp>(loc, toEleTy(cdstTy), load)
133                 .getResult();
134   builder.template create<fir::StoreOp>(loc, cast, out);
135   builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
136   auto space = builder.template create<fir::StringLitOp>(
137       loc, toEleTy(cdstTy), llvm::ArrayRef<char>{' '});
138   auto cdst2 = builder.template create<fir::ConvertOp>(loc, cdstTy, dst);
139   auto out2 = builder.template create<fir::CoordinateOp>(
140       loc, toCoorTy(cdstTy), cdst2, loop.getInductionVar());
141   builder.template create<fir::StoreOp>(loc, space, out2);
142   builder.restoreInsertionPoint(insPt);
143 }
144 
145 /// Get extents from fir.shape/fir.shape_shift op. Empty result if
146 /// \p shapeVal is empty or is a fir.shift.
getExtents(mlir::Value shapeVal)147 inline llvm::SmallVector<mlir::Value> getExtents(mlir::Value shapeVal) {
148   if (shapeVal)
149     if (auto *shapeOp = shapeVal.getDefiningOp()) {
150       if (auto shOp = mlir::dyn_cast<fir::ShapeOp>(shapeOp)) {
151         auto operands = shOp.getExtents();
152         return {operands.begin(), operands.end()};
153       }
154       if (auto shOp = mlir::dyn_cast<fir::ShapeShiftOp>(shapeOp)) {
155         auto operands = shOp.getExtents();
156         return {operands.begin(), operands.end()};
157       }
158     }
159   return {};
160 }
161 
162 /// Get origins from fir.shape_shift/fir.shift op. Empty result if
163 /// \p shapeVal is empty or is a fir.shape.
getOrigins(mlir::Value shapeVal)164 inline llvm::SmallVector<mlir::Value> getOrigins(mlir::Value shapeVal) {
165   if (shapeVal)
166     if (auto *shapeOp = shapeVal.getDefiningOp()) {
167       if (auto shOp = mlir::dyn_cast<fir::ShapeShiftOp>(shapeOp)) {
168         auto operands = shOp.getOrigins();
169         return {operands.begin(), operands.end()};
170       }
171       if (auto shOp = mlir::dyn_cast<fir::ShiftOp>(shapeOp)) {
172         auto operands = shOp.getOrigins();
173         return {operands.begin(), operands.end()};
174       }
175     }
176   return {};
177 }
178 
179 /// Convert the normalized indices on array_fetch and array_update to the
180 /// dynamic (and non-zero) origin required by array_coor.
181 /// Do not adjust any trailing components in the path as they specify a
182 /// particular path into the array value and must already correspond to the
183 /// structure of an element.
184 template <typename B>
185 llvm::SmallVector<mlir::Value>
originateIndices(mlir::Location loc,B & builder,mlir::Type memTy,mlir::Value shapeVal,mlir::ValueRange indices)186 originateIndices(mlir::Location loc, B &builder, mlir::Type memTy,
187                  mlir::Value shapeVal, mlir::ValueRange indices) {
188   llvm::SmallVector<mlir::Value> result;
189   auto origins = getOrigins(shapeVal);
190   if (origins.empty()) {
191     assert(!shapeVal || mlir::isa<fir::ShapeOp>(shapeVal.getDefiningOp()));
192     auto ty = fir::dyn_cast_ptrOrBoxEleTy(memTy);
193     assert(ty && mlir::isa<fir::SequenceType>(ty));
194     auto seqTy = mlir::cast<fir::SequenceType>(ty);
195     auto one = builder.template create<mlir::arith::ConstantIndexOp>(loc, 1);
196     const auto dimension = seqTy.getDimension();
197     if (shapeVal) {
198       assert(dimension == mlir::cast<fir::ShapeOp>(shapeVal.getDefiningOp())
199                               .getType()
200                               .getRank());
201     }
202     for (auto i : llvm::enumerate(indices)) {
203       if (i.index() < dimension) {
204         assert(fir::isa_integer(i.value().getType()));
205         result.push_back(
206             builder.template create<mlir::arith::AddIOp>(loc, i.value(), one));
207       } else {
208         result.push_back(i.value());
209       }
210     }
211     return result;
212   }
213   const auto dimension = origins.size();
214   unsigned origOff = 0;
215   for (auto i : llvm::enumerate(indices)) {
216     if (i.index() < dimension)
217       result.push_back(builder.template create<mlir::arith::AddIOp>(
218           loc, i.value(), origins[origOff++]));
219     else
220       result.push_back(i.value());
221   }
222   return result;
223 }
224 
225 } // namespace fir::factory
226 
227 #endif // FORTRAN_OPTIMIZER_BUILDER_FACTORY_H
228