1 //===-- FIRBuilder.cpp ----------------------------------------------------===//
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 #include "flang/Optimizer/Builder/FIRBuilder.h"
10 #include "flang/Optimizer/Builder/BoxValue.h"
11 #include "flang/Optimizer/Builder/Character.h"
12 #include "flang/Optimizer/Builder/Complex.h"
13 #include "flang/Optimizer/Builder/MutableBox.h"
14 #include "flang/Optimizer/Builder/Runtime/Assign.h"
15 #include "flang/Optimizer/Builder/Todo.h"
16 #include "flang/Optimizer/Dialect/FIRAttr.h"
17 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
18 #include "flang/Optimizer/Support/FatalError.h"
19 #include "flang/Optimizer/Support/InternalNames.h"
20 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
21 #include "llvm/ADT/ArrayRef.h"
22 #include "llvm/ADT/StringExtras.h"
23 #include "llvm/Support/CommandLine.h"
24 #include "llvm/Support/ErrorHandling.h"
25 #include "llvm/Support/MD5.h"
26
27 static llvm::cl::opt<std::size_t>
28 nameLengthHashSize("length-to-hash-string-literal",
29 llvm::cl::desc("string literals that exceed this length"
30 " will use a hash value as their symbol "
31 "name"),
32 llvm::cl::init(32));
33
createFunction(mlir::Location loc,mlir::ModuleOp module,llvm::StringRef name,mlir::FunctionType ty)34 mlir::func::FuncOp fir::FirOpBuilder::createFunction(mlir::Location loc,
35 mlir::ModuleOp module,
36 llvm::StringRef name,
37 mlir::FunctionType ty) {
38 return fir::createFuncOp(loc, module, name, ty);
39 }
40
getNamedFunction(mlir::ModuleOp modOp,llvm::StringRef name)41 mlir::func::FuncOp fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp,
42 llvm::StringRef name) {
43 return modOp.lookupSymbol<mlir::func::FuncOp>(name);
44 }
45
46 mlir::func::FuncOp
getNamedFunction(mlir::ModuleOp modOp,mlir::SymbolRefAttr symbol)47 fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp,
48 mlir::SymbolRefAttr symbol) {
49 return modOp.lookupSymbol<mlir::func::FuncOp>(symbol);
50 }
51
getNamedGlobal(mlir::ModuleOp modOp,llvm::StringRef name)52 fir::GlobalOp fir::FirOpBuilder::getNamedGlobal(mlir::ModuleOp modOp,
53 llvm::StringRef name) {
54 return modOp.lookupSymbol<fir::GlobalOp>(name);
55 }
56
getRefType(mlir::Type eleTy)57 mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy) {
58 assert(!eleTy.isa<fir::ReferenceType>() && "cannot be a reference type");
59 return fir::ReferenceType::get(eleTy);
60 }
61
getVarLenSeqTy(mlir::Type eleTy,unsigned rank)62 mlir::Type fir::FirOpBuilder::getVarLenSeqTy(mlir::Type eleTy, unsigned rank) {
63 fir::SequenceType::Shape shape(rank, fir::SequenceType::getUnknownExtent());
64 return fir::SequenceType::get(shape, eleTy);
65 }
66
getRealType(int kind)67 mlir::Type fir::FirOpBuilder::getRealType(int kind) {
68 switch (kindMap.getRealTypeID(kind)) {
69 case llvm::Type::TypeID::HalfTyID:
70 return mlir::FloatType::getF16(getContext());
71 case llvm::Type::TypeID::FloatTyID:
72 return mlir::FloatType::getF32(getContext());
73 case llvm::Type::TypeID::DoubleTyID:
74 return mlir::FloatType::getF64(getContext());
75 case llvm::Type::TypeID::X86_FP80TyID:
76 return mlir::FloatType::getF80(getContext());
77 case llvm::Type::TypeID::FP128TyID:
78 return mlir::FloatType::getF128(getContext());
79 default:
80 fir::emitFatalError(mlir::UnknownLoc::get(getContext()),
81 "unsupported type !fir.real<kind>");
82 }
83 }
84
createNullConstant(mlir::Location loc,mlir::Type ptrType)85 mlir::Value fir::FirOpBuilder::createNullConstant(mlir::Location loc,
86 mlir::Type ptrType) {
87 auto ty = ptrType ? ptrType : getRefType(getNoneType());
88 return create<fir::ZeroOp>(loc, ty);
89 }
90
createIntegerConstant(mlir::Location loc,mlir::Type ty,std::int64_t cst)91 mlir::Value fir::FirOpBuilder::createIntegerConstant(mlir::Location loc,
92 mlir::Type ty,
93 std::int64_t cst) {
94 return create<mlir::arith::ConstantOp>(loc, ty, getIntegerAttr(ty, cst));
95 }
96
97 mlir::Value
createRealConstant(mlir::Location loc,mlir::Type fltTy,llvm::APFloat::integerPart val)98 fir::FirOpBuilder::createRealConstant(mlir::Location loc, mlir::Type fltTy,
99 llvm::APFloat::integerPart val) {
100 auto apf = [&]() -> llvm::APFloat {
101 if (auto ty = fltTy.dyn_cast<fir::RealType>())
102 return llvm::APFloat(kindMap.getFloatSemantics(ty.getFKind()), val);
103 if (fltTy.isF16())
104 return llvm::APFloat(llvm::APFloat::IEEEhalf(), val);
105 if (fltTy.isBF16())
106 return llvm::APFloat(llvm::APFloat::BFloat(), val);
107 if (fltTy.isF32())
108 return llvm::APFloat(llvm::APFloat::IEEEsingle(), val);
109 if (fltTy.isF64())
110 return llvm::APFloat(llvm::APFloat::IEEEdouble(), val);
111 if (fltTy.isF80())
112 return llvm::APFloat(llvm::APFloat::x87DoubleExtended(), val);
113 if (fltTy.isF128())
114 return llvm::APFloat(llvm::APFloat::IEEEquad(), val);
115 llvm_unreachable("unhandled MLIR floating-point type");
116 };
117 return createRealConstant(loc, fltTy, apf());
118 }
119
createRealConstant(mlir::Location loc,mlir::Type fltTy,const llvm::APFloat & value)120 mlir::Value fir::FirOpBuilder::createRealConstant(mlir::Location loc,
121 mlir::Type fltTy,
122 const llvm::APFloat &value) {
123 if (fltTy.isa<mlir::FloatType>()) {
124 auto attr = getFloatAttr(fltTy, value);
125 return create<mlir::arith::ConstantOp>(loc, fltTy, attr);
126 }
127 llvm_unreachable("should use builtin floating-point type");
128 }
129
130 static llvm::SmallVector<mlir::Value>
elideExtentsAlreadyInType(mlir::Type type,mlir::ValueRange shape)131 elideExtentsAlreadyInType(mlir::Type type, mlir::ValueRange shape) {
132 auto arrTy = type.dyn_cast<fir::SequenceType>();
133 if (shape.empty() || !arrTy)
134 return {};
135 // elide the constant dimensions before construction
136 assert(shape.size() == arrTy.getDimension());
137 llvm::SmallVector<mlir::Value> dynamicShape;
138 auto typeShape = arrTy.getShape();
139 for (unsigned i = 0, end = arrTy.getDimension(); i < end; ++i)
140 if (typeShape[i] == fir::SequenceType::getUnknownExtent())
141 dynamicShape.push_back(shape[i]);
142 return dynamicShape;
143 }
144
145 static llvm::SmallVector<mlir::Value>
elideLengthsAlreadyInType(mlir::Type type,mlir::ValueRange lenParams)146 elideLengthsAlreadyInType(mlir::Type type, mlir::ValueRange lenParams) {
147 if (lenParams.empty())
148 return {};
149 if (auto arrTy = type.dyn_cast<fir::SequenceType>())
150 type = arrTy.getEleTy();
151 if (fir::hasDynamicSize(type))
152 return lenParams;
153 return {};
154 }
155
156 /// Allocate a local variable.
157 /// A local variable ought to have a name in the source code.
allocateLocal(mlir::Location loc,mlir::Type ty,llvm::StringRef uniqName,llvm::StringRef name,bool pinned,llvm::ArrayRef<mlir::Value> shape,llvm::ArrayRef<mlir::Value> lenParams,bool asTarget)158 mlir::Value fir::FirOpBuilder::allocateLocal(
159 mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName,
160 llvm::StringRef name, bool pinned, llvm::ArrayRef<mlir::Value> shape,
161 llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) {
162 // Convert the shape extents to `index`, as needed.
163 llvm::SmallVector<mlir::Value> indices;
164 llvm::SmallVector<mlir::Value> elidedShape =
165 elideExtentsAlreadyInType(ty, shape);
166 llvm::SmallVector<mlir::Value> elidedLenParams =
167 elideLengthsAlreadyInType(ty, lenParams);
168 auto idxTy = getIndexType();
169 llvm::for_each(elidedShape, [&](mlir::Value sh) {
170 indices.push_back(createConvert(loc, idxTy, sh));
171 });
172 // Add a target attribute, if needed.
173 llvm::SmallVector<mlir::NamedAttribute> attrs;
174 if (asTarget)
175 attrs.emplace_back(
176 mlir::StringAttr::get(getContext(), fir::getTargetAttrName()),
177 getUnitAttr());
178 // Create the local variable.
179 if (name.empty()) {
180 if (uniqName.empty())
181 return create<fir::AllocaOp>(loc, ty, pinned, elidedLenParams, indices,
182 attrs);
183 return create<fir::AllocaOp>(loc, ty, uniqName, pinned, elidedLenParams,
184 indices, attrs);
185 }
186 return create<fir::AllocaOp>(loc, ty, uniqName, name, pinned, elidedLenParams,
187 indices, attrs);
188 }
189
allocateLocal(mlir::Location loc,mlir::Type ty,llvm::StringRef uniqName,llvm::StringRef name,llvm::ArrayRef<mlir::Value> shape,llvm::ArrayRef<mlir::Value> lenParams,bool asTarget)190 mlir::Value fir::FirOpBuilder::allocateLocal(
191 mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName,
192 llvm::StringRef name, llvm::ArrayRef<mlir::Value> shape,
193 llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) {
194 return allocateLocal(loc, ty, uniqName, name, /*pinned=*/false, shape,
195 lenParams, asTarget);
196 }
197
198 /// Get the block for adding Allocas.
getAllocaBlock()199 mlir::Block *fir::FirOpBuilder::getAllocaBlock() {
200 auto iface =
201 getRegion().getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>();
202 return iface ? iface.getAllocaBlock() : getEntryBlock();
203 }
204
205 /// Create a temporary variable on the stack. Anonymous temporaries have no
206 /// `name` value. Temporaries do not require a uniqued name.
207 mlir::Value
createTemporary(mlir::Location loc,mlir::Type type,llvm::StringRef name,mlir::ValueRange shape,mlir::ValueRange lenParams,llvm::ArrayRef<mlir::NamedAttribute> attrs)208 fir::FirOpBuilder::createTemporary(mlir::Location loc, mlir::Type type,
209 llvm::StringRef name, mlir::ValueRange shape,
210 mlir::ValueRange lenParams,
211 llvm::ArrayRef<mlir::NamedAttribute> attrs) {
212 llvm::SmallVector<mlir::Value> dynamicShape =
213 elideExtentsAlreadyInType(type, shape);
214 llvm::SmallVector<mlir::Value> dynamicLength =
215 elideLengthsAlreadyInType(type, lenParams);
216 InsertPoint insPt;
217 const bool hoistAlloc = dynamicShape.empty() && dynamicLength.empty();
218 if (hoistAlloc) {
219 insPt = saveInsertionPoint();
220 setInsertionPointToStart(getAllocaBlock());
221 }
222
223 // If the alloca is inside an OpenMP Op which will be outlined then pin the
224 // alloca here.
225 const bool pinned =
226 getRegion().getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>();
227 assert(!type.isa<fir::ReferenceType>() && "cannot be a reference");
228 auto ae =
229 create<fir::AllocaOp>(loc, type, /*unique_name=*/llvm::StringRef{}, name,
230 pinned, dynamicLength, dynamicShape, attrs);
231 if (hoistAlloc)
232 restoreInsertionPoint(insPt);
233 return ae;
234 }
235
236 /// Create a global variable in the (read-only) data section. A global variable
237 /// must have a unique name to identify and reference it.
238 fir::GlobalOp
createGlobal(mlir::Location loc,mlir::Type type,llvm::StringRef name,mlir::StringAttr linkage,mlir::Attribute value,bool isConst)239 fir::FirOpBuilder::createGlobal(mlir::Location loc, mlir::Type type,
240 llvm::StringRef name, mlir::StringAttr linkage,
241 mlir::Attribute value, bool isConst) {
242 auto module = getModule();
243 auto insertPt = saveInsertionPoint();
244 if (auto glob = module.lookupSymbol<fir::GlobalOp>(name))
245 return glob;
246 setInsertionPoint(module.getBody(), module.getBody()->end());
247 auto glob = create<fir::GlobalOp>(loc, name, isConst, type, value, linkage);
248 restoreInsertionPoint(insertPt);
249 return glob;
250 }
251
createGlobal(mlir::Location loc,mlir::Type type,llvm::StringRef name,bool isConst,std::function<void (FirOpBuilder &)> bodyBuilder,mlir::StringAttr linkage)252 fir::GlobalOp fir::FirOpBuilder::createGlobal(
253 mlir::Location loc, mlir::Type type, llvm::StringRef name, bool isConst,
254 std::function<void(FirOpBuilder &)> bodyBuilder, mlir::StringAttr linkage) {
255 auto module = getModule();
256 auto insertPt = saveInsertionPoint();
257 if (auto glob = module.lookupSymbol<fir::GlobalOp>(name))
258 return glob;
259 setInsertionPoint(module.getBody(), module.getBody()->end());
260 auto glob = create<fir::GlobalOp>(loc, name, isConst, type, mlir::Attribute{},
261 linkage);
262 auto ®ion = glob.getRegion();
263 region.push_back(new mlir::Block);
264 auto &block = glob.getRegion().back();
265 setInsertionPointToStart(&block);
266 bodyBuilder(*this);
267 restoreInsertionPoint(insertPt);
268 return glob;
269 }
270
271 mlir::Value
convertWithSemantics(mlir::Location loc,mlir::Type toTy,mlir::Value val,bool allowCharacterConversion)272 fir::FirOpBuilder::convertWithSemantics(mlir::Location loc, mlir::Type toTy,
273 mlir::Value val,
274 bool allowCharacterConversion) {
275 assert(toTy && "store location must be typed");
276 auto fromTy = val.getType();
277 if (fromTy == toTy)
278 return val;
279 fir::factory::Complex helper{*this, loc};
280 if ((fir::isa_real(fromTy) || fir::isa_integer(fromTy)) &&
281 fir::isa_complex(toTy)) {
282 // imaginary part is zero
283 auto eleTy = helper.getComplexPartType(toTy);
284 auto cast = createConvert(loc, eleTy, val);
285 llvm::APFloat zero{
286 kindMap.getFloatSemantics(toTy.cast<fir::ComplexType>().getFKind()), 0};
287 auto imag = createRealConstant(loc, eleTy, zero);
288 return helper.createComplex(toTy, cast, imag);
289 }
290 if (fir::isa_complex(fromTy) &&
291 (fir::isa_integer(toTy) || fir::isa_real(toTy))) {
292 // drop the imaginary part
293 auto rp = helper.extractComplexPart(val, /*isImagPart=*/false);
294 return createConvert(loc, toTy, rp);
295 }
296 if (allowCharacterConversion) {
297 if (fromTy.isa<fir::BoxCharType>()) {
298 // Extract the address of the character string and pass it
299 fir::factory::CharacterExprHelper charHelper{*this, loc};
300 std::pair<mlir::Value, mlir::Value> unboxchar =
301 charHelper.createUnboxChar(val);
302 return createConvert(loc, toTy, unboxchar.first);
303 }
304 if (auto boxType = toTy.dyn_cast<fir::BoxCharType>()) {
305 // Extract the address of the actual argument and create a boxed
306 // character value with an undefined length
307 // TODO: We should really calculate the total size of the actual
308 // argument in characters and use it as the length of the string
309 auto refType = getRefType(boxType.getEleTy());
310 mlir::Value charBase = createConvert(loc, refType, val);
311 mlir::Value unknownLen = create<fir::UndefOp>(loc, getIndexType());
312 fir::factory::CharacterExprHelper charHelper{*this, loc};
313 return charHelper.createEmboxChar(charBase, unknownLen);
314 }
315 }
316 if (fir::isa_ref_type(toTy) && fir::isa_box_type(fromTy)) {
317 // Call is expecting a raw data pointer, not a box. Get the data pointer out
318 // of the box and pass that.
319 assert((fir::unwrapRefType(toTy) ==
320 fir::unwrapRefType(fir::unwrapPassByRefType(fromTy)) &&
321 "element types expected to match"));
322 return create<fir::BoxAddrOp>(loc, toTy, val);
323 }
324
325 return createConvert(loc, toTy, val);
326 }
327
createConvert(mlir::Location loc,mlir::Type toTy,mlir::Value val)328 mlir::Value fir::FirOpBuilder::createConvert(mlir::Location loc,
329 mlir::Type toTy, mlir::Value val) {
330 if (val.getType() != toTy) {
331 assert(!fir::isa_derived(toTy));
332 return create<fir::ConvertOp>(loc, toTy, val);
333 }
334 return val;
335 }
336
createStoreWithConvert(mlir::Location loc,mlir::Value val,mlir::Value addr)337 void fir::FirOpBuilder::createStoreWithConvert(mlir::Location loc,
338 mlir::Value val,
339 mlir::Value addr) {
340 mlir::Value cast =
341 createConvert(loc, fir::unwrapRefType(addr.getType()), val);
342 create<fir::StoreOp>(loc, cast, addr);
343 }
344
createStringLitOp(mlir::Location loc,llvm::StringRef data)345 fir::StringLitOp fir::FirOpBuilder::createStringLitOp(mlir::Location loc,
346 llvm::StringRef data) {
347 auto type = fir::CharacterType::get(getContext(), 1, data.size());
348 auto strAttr = mlir::StringAttr::get(getContext(), data);
349 auto valTag = mlir::StringAttr::get(getContext(), fir::StringLitOp::value());
350 mlir::NamedAttribute dataAttr(valTag, strAttr);
351 auto sizeTag = mlir::StringAttr::get(getContext(), fir::StringLitOp::size());
352 mlir::NamedAttribute sizeAttr(sizeTag, getI64IntegerAttr(data.size()));
353 llvm::SmallVector<mlir::NamedAttribute> attrs{dataAttr, sizeAttr};
354 return create<fir::StringLitOp>(loc, llvm::ArrayRef<mlir::Type>{type},
355 llvm::None, attrs);
356 }
357
genShape(mlir::Location loc,llvm::ArrayRef<mlir::Value> exts)358 mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc,
359 llvm::ArrayRef<mlir::Value> exts) {
360 auto shapeType = fir::ShapeType::get(getContext(), exts.size());
361 return create<fir::ShapeOp>(loc, shapeType, exts);
362 }
363
genShape(mlir::Location loc,llvm::ArrayRef<mlir::Value> shift,llvm::ArrayRef<mlir::Value> exts)364 mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc,
365 llvm::ArrayRef<mlir::Value> shift,
366 llvm::ArrayRef<mlir::Value> exts) {
367 auto shapeType = fir::ShapeShiftType::get(getContext(), exts.size());
368 llvm::SmallVector<mlir::Value> shapeArgs;
369 auto idxTy = getIndexType();
370 for (auto [lbnd, ext] : llvm::zip(shift, exts)) {
371 auto lb = createConvert(loc, idxTy, lbnd);
372 shapeArgs.push_back(lb);
373 shapeArgs.push_back(ext);
374 }
375 return create<fir::ShapeShiftOp>(loc, shapeType, shapeArgs);
376 }
377
genShape(mlir::Location loc,const fir::AbstractArrayBox & arr)378 mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc,
379 const fir::AbstractArrayBox &arr) {
380 if (arr.lboundsAllOne())
381 return genShape(loc, arr.getExtents());
382 return genShape(loc, arr.getLBounds(), arr.getExtents());
383 }
384
createShape(mlir::Location loc,const fir::ExtendedValue & exv)385 mlir::Value fir::FirOpBuilder::createShape(mlir::Location loc,
386 const fir::ExtendedValue &exv) {
387 return exv.match(
388 [&](const fir::ArrayBoxValue &box) { return genShape(loc, box); },
389 [&](const fir::CharArrayBoxValue &box) { return genShape(loc, box); },
390 [&](const fir::BoxValue &box) -> mlir::Value {
391 if (!box.getLBounds().empty()) {
392 auto shiftType =
393 fir::ShiftType::get(getContext(), box.getLBounds().size());
394 return create<fir::ShiftOp>(loc, shiftType, box.getLBounds());
395 }
396 return {};
397 },
398 [&](const fir::MutableBoxValue &) -> mlir::Value {
399 // MutableBoxValue must be read into another category to work with them
400 // outside of allocation/assignment contexts.
401 fir::emitFatalError(loc, "createShape on MutableBoxValue");
402 },
403 [&](auto) -> mlir::Value { fir::emitFatalError(loc, "not an array"); });
404 }
405
createSlice(mlir::Location loc,const fir::ExtendedValue & exv,mlir::ValueRange triples,mlir::ValueRange path)406 mlir::Value fir::FirOpBuilder::createSlice(mlir::Location loc,
407 const fir::ExtendedValue &exv,
408 mlir::ValueRange triples,
409 mlir::ValueRange path) {
410 if (triples.empty()) {
411 // If there is no slicing by triple notation, then take the whole array.
412 auto fullShape = [&](const llvm::ArrayRef<mlir::Value> lbounds,
413 llvm::ArrayRef<mlir::Value> extents) -> mlir::Value {
414 llvm::SmallVector<mlir::Value> trips;
415 auto idxTy = getIndexType();
416 auto one = createIntegerConstant(loc, idxTy, 1);
417 if (lbounds.empty()) {
418 for (auto v : extents) {
419 trips.push_back(one);
420 trips.push_back(v);
421 trips.push_back(one);
422 }
423 return create<fir::SliceOp>(loc, trips, path);
424 }
425 for (auto [lbnd, extent] : llvm::zip(lbounds, extents)) {
426 auto lb = createConvert(loc, idxTy, lbnd);
427 auto ext = createConvert(loc, idxTy, extent);
428 auto shift = create<mlir::arith::SubIOp>(loc, lb, one);
429 auto ub = create<mlir::arith::AddIOp>(loc, ext, shift);
430 trips.push_back(lb);
431 trips.push_back(ub);
432 trips.push_back(one);
433 }
434 return create<fir::SliceOp>(loc, trips, path);
435 };
436 return exv.match(
437 [&](const fir::ArrayBoxValue &box) {
438 return fullShape(box.getLBounds(), box.getExtents());
439 },
440 [&](const fir::CharArrayBoxValue &box) {
441 return fullShape(box.getLBounds(), box.getExtents());
442 },
443 [&](const fir::BoxValue &box) {
444 auto extents = fir::factory::readExtents(*this, loc, box);
445 return fullShape(box.getLBounds(), extents);
446 },
447 [&](const fir::MutableBoxValue &) -> mlir::Value {
448 // MutableBoxValue must be read into another category to work with
449 // them outside of allocation/assignment contexts.
450 fir::emitFatalError(loc, "createSlice on MutableBoxValue");
451 },
452 [&](auto) -> mlir::Value { fir::emitFatalError(loc, "not an array"); });
453 }
454 return create<fir::SliceOp>(loc, triples, path);
455 }
456
createBox(mlir::Location loc,const fir::ExtendedValue & exv)457 mlir::Value fir::FirOpBuilder::createBox(mlir::Location loc,
458 const fir::ExtendedValue &exv) {
459 mlir::Value itemAddr = fir::getBase(exv);
460 if (itemAddr.getType().isa<fir::BoxType>())
461 return itemAddr;
462 auto elementType = fir::dyn_cast_ptrEleTy(itemAddr.getType());
463 if (!elementType) {
464 mlir::emitError(loc, "internal: expected a memory reference type ")
465 << itemAddr.getType();
466 llvm_unreachable("not a memory reference type");
467 }
468 mlir::Type boxTy = fir::BoxType::get(elementType);
469 return exv.match(
470 [&](const fir::ArrayBoxValue &box) -> mlir::Value {
471 mlir::Value s = createShape(loc, exv);
472 return create<fir::EmboxOp>(loc, boxTy, itemAddr, s);
473 },
474 [&](const fir::CharArrayBoxValue &box) -> mlir::Value {
475 mlir::Value s = createShape(loc, exv);
476 if (fir::factory::CharacterExprHelper::hasConstantLengthInType(exv))
477 return create<fir::EmboxOp>(loc, boxTy, itemAddr, s);
478
479 mlir::Value emptySlice;
480 llvm::SmallVector<mlir::Value> lenParams{box.getLen()};
481 return create<fir::EmboxOp>(loc, boxTy, itemAddr, s, emptySlice,
482 lenParams);
483 },
484 [&](const fir::CharBoxValue &box) -> mlir::Value {
485 if (fir::factory::CharacterExprHelper::hasConstantLengthInType(exv))
486 return create<fir::EmboxOp>(loc, boxTy, itemAddr);
487 mlir::Value emptyShape, emptySlice;
488 llvm::SmallVector<mlir::Value> lenParams{box.getLen()};
489 return create<fir::EmboxOp>(loc, boxTy, itemAddr, emptyShape,
490 emptySlice, lenParams);
491 },
492 [&](const fir::MutableBoxValue &x) -> mlir::Value {
493 return create<fir::LoadOp>(
494 loc, fir::factory::getMutableIRBox(*this, loc, x));
495 },
496 [&](const auto &) -> mlir::Value {
497 return create<fir::EmboxOp>(loc, boxTy, itemAddr);
498 });
499 }
500
dumpFunc()501 void fir::FirOpBuilder::dumpFunc() { getFunction().dump(); }
502
503 static mlir::Value
genNullPointerComparison(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Value addr,mlir::arith::CmpIPredicate condition)504 genNullPointerComparison(fir::FirOpBuilder &builder, mlir::Location loc,
505 mlir::Value addr,
506 mlir::arith::CmpIPredicate condition) {
507 auto intPtrTy = builder.getIntPtrType();
508 auto ptrToInt = builder.createConvert(loc, intPtrTy, addr);
509 auto c0 = builder.createIntegerConstant(loc, intPtrTy, 0);
510 return builder.create<mlir::arith::CmpIOp>(loc, condition, ptrToInt, c0);
511 }
512
genIsNotNullAddr(mlir::Location loc,mlir::Value addr)513 mlir::Value fir::FirOpBuilder::genIsNotNullAddr(mlir::Location loc,
514 mlir::Value addr) {
515 return genNullPointerComparison(*this, loc, addr,
516 mlir::arith::CmpIPredicate::ne);
517 }
518
genIsNullAddr(mlir::Location loc,mlir::Value addr)519 mlir::Value fir::FirOpBuilder::genIsNullAddr(mlir::Location loc,
520 mlir::Value addr) {
521 return genNullPointerComparison(*this, loc, addr,
522 mlir::arith::CmpIPredicate::eq);
523 }
524
genExtentFromTriplet(mlir::Location loc,mlir::Value lb,mlir::Value ub,mlir::Value step,mlir::Type type)525 mlir::Value fir::FirOpBuilder::genExtentFromTriplet(mlir::Location loc,
526 mlir::Value lb,
527 mlir::Value ub,
528 mlir::Value step,
529 mlir::Type type) {
530 auto zero = createIntegerConstant(loc, type, 0);
531 lb = createConvert(loc, type, lb);
532 ub = createConvert(loc, type, ub);
533 step = createConvert(loc, type, step);
534 auto diff = create<mlir::arith::SubIOp>(loc, ub, lb);
535 auto add = create<mlir::arith::AddIOp>(loc, diff, step);
536 auto div = create<mlir::arith::DivSIOp>(loc, add, step);
537 auto cmp = create<mlir::arith::CmpIOp>(loc, mlir::arith::CmpIPredicate::sgt,
538 div, zero);
539 return create<mlir::arith::SelectOp>(loc, cmp, div, zero);
540 }
541
542 //===--------------------------------------------------------------------===//
543 // ExtendedValue inquiry helper implementation
544 //===--------------------------------------------------------------------===//
545
readCharLen(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & box)546 mlir::Value fir::factory::readCharLen(fir::FirOpBuilder &builder,
547 mlir::Location loc,
548 const fir::ExtendedValue &box) {
549 return box.match(
550 [&](const fir::CharBoxValue &x) -> mlir::Value { return x.getLen(); },
551 [&](const fir::CharArrayBoxValue &x) -> mlir::Value {
552 return x.getLen();
553 },
554 [&](const fir::BoxValue &x) -> mlir::Value {
555 assert(x.isCharacter());
556 if (!x.getExplicitParameters().empty())
557 return x.getExplicitParameters()[0];
558 return fir::factory::CharacterExprHelper{builder, loc}
559 .readLengthFromBox(x.getAddr());
560 },
561 [&](const fir::MutableBoxValue &x) -> mlir::Value {
562 return readCharLen(builder, loc,
563 fir::factory::genMutableBoxRead(builder, loc, x));
564 },
565 [&](const auto &) -> mlir::Value {
566 fir::emitFatalError(
567 loc, "Character length inquiry on a non-character entity");
568 });
569 }
570
readExtent(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & box,unsigned dim)571 mlir::Value fir::factory::readExtent(fir::FirOpBuilder &builder,
572 mlir::Location loc,
573 const fir::ExtendedValue &box,
574 unsigned dim) {
575 assert(box.rank() > dim);
576 return box.match(
577 [&](const fir::ArrayBoxValue &x) -> mlir::Value {
578 return x.getExtents()[dim];
579 },
580 [&](const fir::CharArrayBoxValue &x) -> mlir::Value {
581 return x.getExtents()[dim];
582 },
583 [&](const fir::BoxValue &x) -> mlir::Value {
584 if (!x.getExplicitExtents().empty())
585 return x.getExplicitExtents()[dim];
586 auto idxTy = builder.getIndexType();
587 auto dimVal = builder.createIntegerConstant(loc, idxTy, dim);
588 return builder
589 .create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, x.getAddr(),
590 dimVal)
591 .getResult(1);
592 },
593 [&](const fir::MutableBoxValue &x) -> mlir::Value {
594 return readExtent(builder, loc,
595 fir::factory::genMutableBoxRead(builder, loc, x),
596 dim);
597 },
598 [&](const auto &) -> mlir::Value {
599 fir::emitFatalError(loc, "extent inquiry on scalar");
600 });
601 }
602
readLowerBound(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & box,unsigned dim,mlir::Value defaultValue)603 mlir::Value fir::factory::readLowerBound(fir::FirOpBuilder &builder,
604 mlir::Location loc,
605 const fir::ExtendedValue &box,
606 unsigned dim,
607 mlir::Value defaultValue) {
608 assert(box.rank() > dim);
609 auto lb = box.match(
610 [&](const fir::ArrayBoxValue &x) -> mlir::Value {
611 return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim];
612 },
613 [&](const fir::CharArrayBoxValue &x) -> mlir::Value {
614 return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim];
615 },
616 [&](const fir::BoxValue &x) -> mlir::Value {
617 return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim];
618 },
619 [&](const fir::MutableBoxValue &x) -> mlir::Value {
620 return readLowerBound(builder, loc,
621 fir::factory::genMutableBoxRead(builder, loc, x),
622 dim, defaultValue);
623 },
624 [&](const auto &) -> mlir::Value {
625 fir::emitFatalError(loc, "lower bound inquiry on scalar");
626 });
627 if (lb)
628 return lb;
629 return defaultValue;
630 }
631
632 llvm::SmallVector<mlir::Value>
readExtents(fir::FirOpBuilder & builder,mlir::Location loc,const fir::BoxValue & box)633 fir::factory::readExtents(fir::FirOpBuilder &builder, mlir::Location loc,
634 const fir::BoxValue &box) {
635 llvm::SmallVector<mlir::Value> result;
636 auto explicitExtents = box.getExplicitExtents();
637 if (!explicitExtents.empty()) {
638 result.append(explicitExtents.begin(), explicitExtents.end());
639 return result;
640 }
641 auto rank = box.rank();
642 auto idxTy = builder.getIndexType();
643 for (decltype(rank) dim = 0; dim < rank; ++dim) {
644 auto dimVal = builder.createIntegerConstant(loc, idxTy, dim);
645 auto dimInfo = builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy,
646 box.getAddr(), dimVal);
647 result.emplace_back(dimInfo.getResult(1));
648 }
649 return result;
650 }
651
652 llvm::SmallVector<mlir::Value>
getExtents(mlir::Location loc,fir::FirOpBuilder & builder,const fir::ExtendedValue & box)653 fir::factory::getExtents(mlir::Location loc, fir::FirOpBuilder &builder,
654 const fir::ExtendedValue &box) {
655 return box.match(
656 [&](const fir::ArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> {
657 return {x.getExtents().begin(), x.getExtents().end()};
658 },
659 [&](const fir::CharArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> {
660 return {x.getExtents().begin(), x.getExtents().end()};
661 },
662 [&](const fir::BoxValue &x) -> llvm::SmallVector<mlir::Value> {
663 return fir::factory::readExtents(builder, loc, x);
664 },
665 [&](const fir::MutableBoxValue &x) -> llvm::SmallVector<mlir::Value> {
666 auto load = fir::factory::genMutableBoxRead(builder, loc, x);
667 return fir::factory::getExtents(loc, builder, load);
668 },
669 [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; });
670 }
671
readBoxValue(fir::FirOpBuilder & builder,mlir::Location loc,const fir::BoxValue & box)672 fir::ExtendedValue fir::factory::readBoxValue(fir::FirOpBuilder &builder,
673 mlir::Location loc,
674 const fir::BoxValue &box) {
675 assert(!box.isUnlimitedPolymorphic() && !box.hasAssumedRank() &&
676 "cannot read unlimited polymorphic or assumed rank fir.box");
677 auto addr =
678 builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr());
679 if (box.isCharacter()) {
680 auto len = fir::factory::readCharLen(builder, loc, box);
681 if (box.rank() == 0)
682 return fir::CharBoxValue(addr, len);
683 return fir::CharArrayBoxValue(addr, len,
684 fir::factory::readExtents(builder, loc, box),
685 box.getLBounds());
686 }
687 if (box.isDerivedWithLenParameters())
688 TODO(loc, "read fir.box with length parameters");
689 if (box.rank() == 0)
690 return addr;
691 return fir::ArrayBoxValue(addr, fir::factory::readExtents(builder, loc, box),
692 box.getLBounds());
693 }
694
695 llvm::SmallVector<mlir::Value>
getNonDefaultLowerBounds(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & exv)696 fir::factory::getNonDefaultLowerBounds(fir::FirOpBuilder &builder,
697 mlir::Location loc,
698 const fir::ExtendedValue &exv) {
699 return exv.match(
700 [&](const fir::ArrayBoxValue &array) -> llvm::SmallVector<mlir::Value> {
701 return {array.getLBounds().begin(), array.getLBounds().end()};
702 },
703 [&](const fir::CharArrayBoxValue &array)
704 -> llvm::SmallVector<mlir::Value> {
705 return {array.getLBounds().begin(), array.getLBounds().end()};
706 },
707 [&](const fir::BoxValue &box) -> llvm::SmallVector<mlir::Value> {
708 return {box.getLBounds().begin(), box.getLBounds().end()};
709 },
710 [&](const fir::MutableBoxValue &box) -> llvm::SmallVector<mlir::Value> {
711 auto load = fir::factory::genMutableBoxRead(builder, loc, box);
712 return fir::factory::getNonDefaultLowerBounds(builder, loc, load);
713 },
714 [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; });
715 }
716
717 llvm::SmallVector<mlir::Value>
getNonDeferredLenParams(const fir::ExtendedValue & exv)718 fir::factory::getNonDeferredLenParams(const fir::ExtendedValue &exv) {
719 return exv.match(
720 [&](const fir::CharArrayBoxValue &character)
721 -> llvm::SmallVector<mlir::Value> { return {character.getLen()}; },
722 [&](const fir::CharBoxValue &character)
723 -> llvm::SmallVector<mlir::Value> { return {character.getLen()}; },
724 [&](const fir::MutableBoxValue &box) -> llvm::SmallVector<mlir::Value> {
725 return {box.nonDeferredLenParams().begin(),
726 box.nonDeferredLenParams().end()};
727 },
728 [&](const fir::BoxValue &box) -> llvm::SmallVector<mlir::Value> {
729 return {box.getExplicitParameters().begin(),
730 box.getExplicitParameters().end()};
731 },
732 [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; });
733 }
734
735 // If valTy is a box type, then we need to extract the type parameters from
736 // the box value.
getFromBox(mlir::Location loc,fir::FirOpBuilder & builder,mlir::Type valTy,mlir::Value boxVal)737 static llvm::SmallVector<mlir::Value> getFromBox(mlir::Location loc,
738 fir::FirOpBuilder &builder,
739 mlir::Type valTy,
740 mlir::Value boxVal) {
741 if (auto boxTy = valTy.dyn_cast<fir::BoxType>()) {
742 auto eleTy = fir::unwrapAllRefAndSeqType(boxTy.getEleTy());
743 if (auto recTy = eleTy.dyn_cast<fir::RecordType>()) {
744 if (recTy.getNumLenParams() > 0) {
745 // Walk each type parameter in the record and get the value.
746 TODO(loc, "generate code to get LEN type parameters");
747 }
748 } else if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
749 if (charTy.hasDynamicLen()) {
750 auto idxTy = builder.getIndexType();
751 auto eleSz = builder.create<fir::BoxEleSizeOp>(loc, idxTy, boxVal);
752 auto kindBytes =
753 builder.getKindMap().getCharacterBitsize(charTy.getFKind()) / 8;
754 mlir::Value charSz =
755 builder.createIntegerConstant(loc, idxTy, kindBytes);
756 mlir::Value len =
757 builder.create<mlir::arith::DivSIOp>(loc, eleSz, charSz);
758 return {len};
759 }
760 }
761 }
762 return {};
763 }
764
765 // fir::getTypeParams() will get the type parameters from the extended value.
766 // When the extended value is a BoxValue or MutableBoxValue, it may be necessary
767 // to generate code, so this factory function handles those cases.
768 // TODO: fix the inverted type tests, etc.
769 llvm::SmallVector<mlir::Value>
getTypeParams(mlir::Location loc,fir::FirOpBuilder & builder,const fir::ExtendedValue & exv)770 fir::factory::getTypeParams(mlir::Location loc, fir::FirOpBuilder &builder,
771 const fir::ExtendedValue &exv) {
772 auto handleBoxed = [&](const auto &box) -> llvm::SmallVector<mlir::Value> {
773 if (box.isCharacter())
774 return {fir::factory::readCharLen(builder, loc, exv)};
775 if (box.isDerivedWithLenParameters()) {
776 // This should generate code to read the type parameters from the box.
777 // This requires some consideration however as MutableBoxValues need to be
778 // in a sane state to be provide the correct values.
779 TODO(loc, "derived type with type parameters");
780 }
781 return {};
782 };
783 // Intentionally reuse the original code path to get type parameters for the
784 // cases that were supported rather than introduce a new path.
785 return exv.match(
786 [&](const fir::BoxValue &box) { return handleBoxed(box); },
787 [&](const fir::MutableBoxValue &box) { return handleBoxed(box); },
788 [&](const auto &) { return fir::getTypeParams(exv); });
789 }
790
791 llvm::SmallVector<mlir::Value>
getTypeParams(mlir::Location loc,fir::FirOpBuilder & builder,fir::ArrayLoadOp load)792 fir::factory::getTypeParams(mlir::Location loc, fir::FirOpBuilder &builder,
793 fir::ArrayLoadOp load) {
794 mlir::Type memTy = load.getMemref().getType();
795 if (auto boxTy = memTy.dyn_cast<fir::BoxType>())
796 return getFromBox(loc, builder, boxTy, load.getMemref());
797 return load.getTypeparams();
798 }
799
uniqueCGIdent(llvm::StringRef prefix,llvm::StringRef name)800 std::string fir::factory::uniqueCGIdent(llvm::StringRef prefix,
801 llvm::StringRef name) {
802 // For "long" identifiers use a hash value
803 if (name.size() > nameLengthHashSize) {
804 llvm::MD5 hash;
805 hash.update(name);
806 llvm::MD5::MD5Result result;
807 hash.final(result);
808 llvm::SmallString<32> str;
809 llvm::MD5::stringifyResult(result, str);
810 std::string hashName = prefix.str();
811 hashName.append(".").append(str.c_str());
812 return fir::NameUniquer::doGenerated(hashName);
813 }
814 // "Short" identifiers use a reversible hex string
815 std::string nm = prefix.str();
816 return fir::NameUniquer::doGenerated(
817 nm.append(".").append(llvm::toHex(name)));
818 }
819
locationToFilename(fir::FirOpBuilder & builder,mlir::Location loc)820 mlir::Value fir::factory::locationToFilename(fir::FirOpBuilder &builder,
821 mlir::Location loc) {
822 if (auto flc = loc.dyn_cast<mlir::FileLineColLoc>()) {
823 // must be encoded as asciiz, C string
824 auto fn = flc.getFilename().str() + '\0';
825 return fir::getBase(createStringLiteral(builder, loc, fn));
826 }
827 return builder.createNullConstant(loc);
828 }
829
locationToLineNo(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Type type)830 mlir::Value fir::factory::locationToLineNo(fir::FirOpBuilder &builder,
831 mlir::Location loc,
832 mlir::Type type) {
833 if (auto flc = loc.dyn_cast<mlir::FileLineColLoc>())
834 return builder.createIntegerConstant(loc, type, flc.getLine());
835 return builder.createIntegerConstant(loc, type, 0);
836 }
837
createStringLiteral(fir::FirOpBuilder & builder,mlir::Location loc,llvm::StringRef str)838 fir::ExtendedValue fir::factory::createStringLiteral(fir::FirOpBuilder &builder,
839 mlir::Location loc,
840 llvm::StringRef str) {
841 std::string globalName = fir::factory::uniqueCGIdent("cl", str);
842 auto type = fir::CharacterType::get(builder.getContext(), 1, str.size());
843 auto global = builder.getNamedGlobal(globalName);
844 if (!global)
845 global = builder.createGlobalConstant(
846 loc, type, globalName,
847 [&](fir::FirOpBuilder &builder) {
848 auto stringLitOp = builder.createStringLitOp(loc, str);
849 builder.create<fir::HasValueOp>(loc, stringLitOp);
850 },
851 builder.createLinkOnceLinkage());
852 auto addr = builder.create<fir::AddrOfOp>(loc, global.resultType(),
853 global.getSymbol());
854 auto len = builder.createIntegerConstant(
855 loc, builder.getCharacterLengthType(), str.size());
856 return fir::CharBoxValue{addr, len};
857 }
858
859 llvm::SmallVector<mlir::Value>
createExtents(fir::FirOpBuilder & builder,mlir::Location loc,fir::SequenceType seqTy)860 fir::factory::createExtents(fir::FirOpBuilder &builder, mlir::Location loc,
861 fir::SequenceType seqTy) {
862 llvm::SmallVector<mlir::Value> extents;
863 auto idxTy = builder.getIndexType();
864 for (auto ext : seqTy.getShape())
865 extents.emplace_back(
866 ext == fir::SequenceType::getUnknownExtent()
867 ? builder.create<fir::UndefOp>(loc, idxTy).getResult()
868 : builder.createIntegerConstant(loc, idxTy, ext));
869 return extents;
870 }
871
872 // FIXME: This needs some work. To correctly determine the extended value of a
873 // component, one needs the base object, its type, and its type parameters. (An
874 // alternative would be to provide an already computed address of the final
875 // component rather than the base object's address, the point being the result
876 // will require the address of the final component to create the extended
877 // value.) One further needs the full path of components being applied. One
878 // needs to apply type-based expressions to type parameters along this said
879 // path. (See applyPathToType for a type-only derivation.) Finally, one needs to
880 // compose the extended value of the terminal component, including all of its
881 // parameters: array lower bounds expressions, extents, type parameters, etc.
882 // Any of these properties may be deferred until runtime in Fortran. This
883 // operation may therefore generate a sizeable block of IR, including calls to
884 // type-based helper functions, so caching the result of this operation in the
885 // client would be advised as well.
componentToExtendedValue(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Value component)886 fir::ExtendedValue fir::factory::componentToExtendedValue(
887 fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value component) {
888 auto fieldTy = component.getType();
889 if (auto ty = fir::dyn_cast_ptrEleTy(fieldTy))
890 fieldTy = ty;
891 if (fieldTy.isa<fir::BoxType>()) {
892 llvm::SmallVector<mlir::Value> nonDeferredTypeParams;
893 auto eleTy = fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(fieldTy));
894 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
895 auto lenTy = builder.getCharacterLengthType();
896 if (charTy.hasConstantLen())
897 nonDeferredTypeParams.emplace_back(
898 builder.createIntegerConstant(loc, lenTy, charTy.getLen()));
899 // TODO: Starting, F2003, the dynamic character length might be dependent
900 // on a PDT length parameter. There is no way to make a difference with
901 // deferred length here yet.
902 }
903 if (auto recTy = eleTy.dyn_cast<fir::RecordType>())
904 if (recTy.getNumLenParams() > 0)
905 TODO(loc, "allocatable and pointer components non deferred length "
906 "parameters");
907
908 return fir::MutableBoxValue(component, nonDeferredTypeParams,
909 /*mutableProperties=*/{});
910 }
911 llvm::SmallVector<mlir::Value> extents;
912 if (auto seqTy = fieldTy.dyn_cast<fir::SequenceType>()) {
913 fieldTy = seqTy.getEleTy();
914 auto idxTy = builder.getIndexType();
915 for (auto extent : seqTy.getShape()) {
916 if (extent == fir::SequenceType::getUnknownExtent())
917 TODO(loc, "array component shape depending on length parameters");
918 extents.emplace_back(builder.createIntegerConstant(loc, idxTy, extent));
919 }
920 }
921 if (auto charTy = fieldTy.dyn_cast<fir::CharacterType>()) {
922 auto cstLen = charTy.getLen();
923 if (cstLen == fir::CharacterType::unknownLen())
924 TODO(loc, "get character component length from length type parameters");
925 auto len = builder.createIntegerConstant(
926 loc, builder.getCharacterLengthType(), cstLen);
927 if (!extents.empty())
928 return fir::CharArrayBoxValue{component, len, extents};
929 return fir::CharBoxValue{component, len};
930 }
931 if (auto recordTy = fieldTy.dyn_cast<fir::RecordType>())
932 if (recordTy.getNumLenParams() != 0)
933 TODO(loc,
934 "lower component ref that is a derived type with length parameter");
935 if (!extents.empty())
936 return fir::ArrayBoxValue{component, extents};
937 return component;
938 }
939
arrayElementToExtendedValue(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & array,mlir::Value element)940 fir::ExtendedValue fir::factory::arrayElementToExtendedValue(
941 fir::FirOpBuilder &builder, mlir::Location loc,
942 const fir::ExtendedValue &array, mlir::Value element) {
943 return array.match(
944 [&](const fir::CharBoxValue &cb) -> fir::ExtendedValue {
945 return cb.clone(element);
946 },
947 [&](const fir::CharArrayBoxValue &bv) -> fir::ExtendedValue {
948 return bv.cloneElement(element);
949 },
950 [&](const fir::BoxValue &box) -> fir::ExtendedValue {
951 if (box.isCharacter()) {
952 auto len = fir::factory::readCharLen(builder, loc, box);
953 return fir::CharBoxValue{element, len};
954 }
955 if (box.isDerivedWithLenParameters())
956 TODO(loc, "get length parameters from derived type BoxValue");
957 return element;
958 },
959 [&](const auto &) -> fir::ExtendedValue { return element; });
960 }
961
arraySectionElementToExtendedValue(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & array,mlir::Value element,mlir::Value slice)962 fir::ExtendedValue fir::factory::arraySectionElementToExtendedValue(
963 fir::FirOpBuilder &builder, mlir::Location loc,
964 const fir::ExtendedValue &array, mlir::Value element, mlir::Value slice) {
965 if (!slice)
966 return arrayElementToExtendedValue(builder, loc, array, element);
967 auto sliceOp = mlir::dyn_cast_or_null<fir::SliceOp>(slice.getDefiningOp());
968 assert(sliceOp && "slice must be a sliceOp");
969 if (sliceOp.getFields().empty())
970 return arrayElementToExtendedValue(builder, loc, array, element);
971 // For F95, using componentToExtendedValue will work, but when PDTs are
972 // lowered. It will be required to go down the slice to propagate the length
973 // parameters.
974 return fir::factory::componentToExtendedValue(builder, loc, element);
975 }
976
genScalarAssignment(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & lhs,const fir::ExtendedValue & rhs)977 void fir::factory::genScalarAssignment(fir::FirOpBuilder &builder,
978 mlir::Location loc,
979 const fir::ExtendedValue &lhs,
980 const fir::ExtendedValue &rhs) {
981 assert(lhs.rank() == 0 && rhs.rank() == 0 && "must be scalars");
982 auto type = fir::unwrapSequenceType(
983 fir::unwrapPassByRefType(fir::getBase(lhs).getType()));
984 if (type.isa<fir::CharacterType>()) {
985 const fir::CharBoxValue *toChar = lhs.getCharBox();
986 const fir::CharBoxValue *fromChar = rhs.getCharBox();
987 assert(toChar && fromChar);
988 fir::factory::CharacterExprHelper helper{builder, loc};
989 helper.createAssign(fir::ExtendedValue{*toChar},
990 fir::ExtendedValue{*fromChar});
991 } else if (type.isa<fir::RecordType>()) {
992 fir::factory::genRecordAssignment(builder, loc, lhs, rhs);
993 } else {
994 assert(!fir::hasDynamicSize(type));
995 auto rhsVal = fir::getBase(rhs);
996 if (fir::isa_ref_type(rhsVal.getType()))
997 rhsVal = builder.create<fir::LoadOp>(loc, rhsVal);
998 mlir::Value lhsAddr = fir::getBase(lhs);
999 rhsVal = builder.createConvert(loc, fir::unwrapRefType(lhsAddr.getType()),
1000 rhsVal);
1001 builder.create<fir::StoreOp>(loc, rhsVal, lhsAddr);
1002 }
1003 }
1004
genComponentByComponentAssignment(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & lhs,const fir::ExtendedValue & rhs)1005 static void genComponentByComponentAssignment(fir::FirOpBuilder &builder,
1006 mlir::Location loc,
1007 const fir::ExtendedValue &lhs,
1008 const fir::ExtendedValue &rhs) {
1009 auto lbaseType = fir::unwrapPassByRefType(fir::getBase(lhs).getType());
1010 auto lhsType = lbaseType.dyn_cast<fir::RecordType>();
1011 assert(lhsType && "lhs must be a scalar record type");
1012 auto rbaseType = fir::unwrapPassByRefType(fir::getBase(rhs).getType());
1013 auto rhsType = rbaseType.dyn_cast<fir::RecordType>();
1014 assert(rhsType && "rhs must be a scalar record type");
1015 auto fieldIndexType = fir::FieldType::get(lhsType.getContext());
1016 for (auto [lhsPair, rhsPair] :
1017 llvm::zip(lhsType.getTypeList(), rhsType.getTypeList())) {
1018 auto &[lFieldName, lFieldTy] = lhsPair;
1019 auto &[rFieldName, rFieldTy] = rhsPair;
1020 assert(!fir::hasDynamicSize(lFieldTy) && !fir::hasDynamicSize(rFieldTy));
1021 mlir::Value rField = builder.create<fir::FieldIndexOp>(
1022 loc, fieldIndexType, rFieldName, rhsType, fir::getTypeParams(rhs));
1023 auto rFieldRefType = builder.getRefType(rFieldTy);
1024 mlir::Value fromCoor = builder.create<fir::CoordinateOp>(
1025 loc, rFieldRefType, fir::getBase(rhs), rField);
1026 mlir::Value field = builder.create<fir::FieldIndexOp>(
1027 loc, fieldIndexType, lFieldName, lhsType, fir::getTypeParams(lhs));
1028 auto fieldRefType = builder.getRefType(lFieldTy);
1029 mlir::Value toCoor = builder.create<fir::CoordinateOp>(
1030 loc, fieldRefType, fir::getBase(lhs), field);
1031 llvm::Optional<fir::DoLoopOp> outerLoop;
1032 if (auto sequenceType = lFieldTy.dyn_cast<fir::SequenceType>()) {
1033 // Create loops to assign array components elements by elements.
1034 // Note that, since these are components, they either do not overlap,
1035 // or are the same and exactly overlap. They also have compile time
1036 // constant shapes.
1037 mlir::Type idxTy = builder.getIndexType();
1038 llvm::SmallVector<mlir::Value> indices;
1039 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
1040 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
1041 for (auto extent : llvm::reverse(sequenceType.getShape())) {
1042 // TODO: add zero size test !
1043 mlir::Value ub = builder.createIntegerConstant(loc, idxTy, extent - 1);
1044 auto loop = builder.create<fir::DoLoopOp>(loc, zero, ub, one);
1045 if (!outerLoop)
1046 outerLoop = loop;
1047 indices.push_back(loop.getInductionVar());
1048 builder.setInsertionPointToStart(loop.getBody());
1049 }
1050 // Set indices in column-major order.
1051 std::reverse(indices.begin(), indices.end());
1052 auto elementRefType = builder.getRefType(sequenceType.getEleTy());
1053 toCoor = builder.create<fir::CoordinateOp>(loc, elementRefType, toCoor,
1054 indices);
1055 fromCoor = builder.create<fir::CoordinateOp>(loc, elementRefType,
1056 fromCoor, indices);
1057 }
1058 if (auto fieldEleTy = fir::unwrapSequenceType(lFieldTy);
1059 fieldEleTy.isa<fir::BoxType>()) {
1060 assert(
1061 fieldEleTy.cast<fir::BoxType>().getEleTy().isa<fir::PointerType>() &&
1062 "allocatable members require deep copy");
1063 auto fromPointerValue = builder.create<fir::LoadOp>(loc, fromCoor);
1064 auto castTo = builder.createConvert(loc, fieldEleTy, fromPointerValue);
1065 builder.create<fir::StoreOp>(loc, castTo, toCoor);
1066 } else {
1067 auto from =
1068 fir::factory::componentToExtendedValue(builder, loc, fromCoor);
1069 auto to = fir::factory::componentToExtendedValue(builder, loc, toCoor);
1070 fir::factory::genScalarAssignment(builder, loc, to, from);
1071 }
1072 if (outerLoop)
1073 builder.setInsertionPointAfter(*outerLoop);
1074 }
1075 }
1076
1077 /// Can the assignment of this record type be implement with a simple memory
1078 /// copy (it requires no deep copy or user defined assignment of components )?
recordTypeCanBeMemCopied(fir::RecordType recordType)1079 static bool recordTypeCanBeMemCopied(fir::RecordType recordType) {
1080 if (fir::hasDynamicSize(recordType))
1081 return false;
1082 for (auto [_, fieldType] : recordType.getTypeList()) {
1083 // Derived type component may have user assignment (so far, we cannot tell
1084 // in FIR, so assume it is always the case, TODO: get the actual info).
1085 if (fir::unwrapSequenceType(fieldType).isa<fir::RecordType>())
1086 return false;
1087 // Allocatable components need deep copy.
1088 if (auto boxType = fieldType.dyn_cast<fir::BoxType>())
1089 if (boxType.getEleTy().isa<fir::HeapType>())
1090 return false;
1091 }
1092 // Constant size components without user defined assignment and pointers can
1093 // be memcopied.
1094 return true;
1095 }
1096
genRecordAssignment(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & lhs,const fir::ExtendedValue & rhs)1097 void fir::factory::genRecordAssignment(fir::FirOpBuilder &builder,
1098 mlir::Location loc,
1099 const fir::ExtendedValue &lhs,
1100 const fir::ExtendedValue &rhs) {
1101 assert(lhs.rank() == 0 && rhs.rank() == 0 && "assume scalar assignment");
1102 auto baseTy = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(lhs).getType());
1103 assert(baseTy && "must be a memory type");
1104 // Box operands may be polymorphic, it is not entirely clear from 10.2.1.3
1105 // if the assignment is performed on the dynamic of declared type. Use the
1106 // runtime assuming it is performed on the dynamic type.
1107 bool hasBoxOperands = fir::getBase(lhs).getType().isa<fir::BoxType>() ||
1108 fir::getBase(rhs).getType().isa<fir::BoxType>();
1109 auto recTy = baseTy.dyn_cast<fir::RecordType>();
1110 assert(recTy && "must be a record type");
1111 if (hasBoxOperands || !recordTypeCanBeMemCopied(recTy)) {
1112 auto to = fir::getBase(builder.createBox(loc, lhs));
1113 auto from = fir::getBase(builder.createBox(loc, rhs));
1114 // The runtime entry point may modify the LHS descriptor if it is
1115 // an allocatable. Allocatable assignment is handle elsewhere in lowering,
1116 // so just create a fir.ref<fir.box<>> from the fir.box to comply with the
1117 // runtime interface, but assume the fir.box is unchanged.
1118 // TODO: does this holds true with polymorphic entities ?
1119 auto toMutableBox = builder.createTemporary(loc, to.getType());
1120 builder.create<fir::StoreOp>(loc, to, toMutableBox);
1121 fir::runtime::genAssign(builder, loc, toMutableBox, from);
1122 return;
1123 }
1124 // Otherwise, the derived type has compile time constant size and for which
1125 // the component by component assignment can be replaced by a memory copy.
1126 // Since we do not know the size of the derived type in lowering, do a
1127 // component by component assignment. Note that a single fir.load/fir.store
1128 // could be used on "small" record types, but as the type size grows, this
1129 // leads to issues in LLVM (long compile times, long IR files, and even
1130 // asserts at some point). Since there is no good size boundary, just always
1131 // use component by component assignment here.
1132 genComponentByComponentAssignment(builder, loc, lhs, rhs);
1133 }
1134
1135 mlir::TupleType
getRaggedArrayHeaderType(fir::FirOpBuilder & builder)1136 fir::factory::getRaggedArrayHeaderType(fir::FirOpBuilder &builder) {
1137 mlir::IntegerType i64Ty = builder.getIntegerType(64);
1138 auto arrTy = fir::SequenceType::get(builder.getIntegerType(8), 1);
1139 auto buffTy = fir::HeapType::get(arrTy);
1140 auto extTy = fir::SequenceType::get(i64Ty, 1);
1141 auto shTy = fir::HeapType::get(extTy);
1142 return mlir::TupleType::get(builder.getContext(), {i64Ty, buffTy, shTy});
1143 }
1144
genLenOfCharacter(fir::FirOpBuilder & builder,mlir::Location loc,fir::ArrayLoadOp arrLoad,llvm::ArrayRef<mlir::Value> path,llvm::ArrayRef<mlir::Value> substring)1145 mlir::Value fir::factory::genLenOfCharacter(
1146 fir::FirOpBuilder &builder, mlir::Location loc, fir::ArrayLoadOp arrLoad,
1147 llvm::ArrayRef<mlir::Value> path, llvm::ArrayRef<mlir::Value> substring) {
1148 llvm::SmallVector<mlir::Value> typeParams(arrLoad.getTypeparams());
1149 return genLenOfCharacter(builder, loc,
1150 arrLoad.getType().cast<fir::SequenceType>(),
1151 arrLoad.getMemref(), typeParams, path, substring);
1152 }
1153
genLenOfCharacter(fir::FirOpBuilder & builder,mlir::Location loc,fir::SequenceType seqTy,mlir::Value memref,llvm::ArrayRef<mlir::Value> typeParams,llvm::ArrayRef<mlir::Value> path,llvm::ArrayRef<mlir::Value> substring)1154 mlir::Value fir::factory::genLenOfCharacter(
1155 fir::FirOpBuilder &builder, mlir::Location loc, fir::SequenceType seqTy,
1156 mlir::Value memref, llvm::ArrayRef<mlir::Value> typeParams,
1157 llvm::ArrayRef<mlir::Value> path, llvm::ArrayRef<mlir::Value> substring) {
1158 auto idxTy = builder.getIndexType();
1159 auto zero = builder.createIntegerConstant(loc, idxTy, 0);
1160 auto saturatedDiff = [&](mlir::Value lower, mlir::Value upper) {
1161 auto diff = builder.create<mlir::arith::SubIOp>(loc, upper, lower);
1162 auto one = builder.createIntegerConstant(loc, idxTy, 1);
1163 auto size = builder.create<mlir::arith::AddIOp>(loc, diff, one);
1164 auto cmp = builder.create<mlir::arith::CmpIOp>(
1165 loc, mlir::arith::CmpIPredicate::sgt, size, zero);
1166 return builder.create<mlir::arith::SelectOp>(loc, cmp, size, zero);
1167 };
1168 if (substring.size() == 2) {
1169 auto upper = builder.createConvert(loc, idxTy, substring.back());
1170 auto lower = builder.createConvert(loc, idxTy, substring.front());
1171 return saturatedDiff(lower, upper);
1172 }
1173 auto lower = zero;
1174 if (substring.size() == 1)
1175 lower = builder.createConvert(loc, idxTy, substring.front());
1176 auto eleTy = fir::applyPathToType(seqTy, path);
1177 if (!fir::hasDynamicSize(eleTy)) {
1178 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
1179 // Use LEN from the type.
1180 return builder.createIntegerConstant(loc, idxTy, charTy.getLen());
1181 }
1182 // Do we need to support !fir.array<!fir.char<k,n>>?
1183 fir::emitFatalError(loc,
1184 "application of path did not result in a !fir.char");
1185 }
1186 if (fir::isa_box_type(memref.getType())) {
1187 if (memref.getType().isa<fir::BoxCharType>())
1188 return builder.create<fir::BoxCharLenOp>(loc, idxTy, memref);
1189 if (memref.getType().isa<fir::BoxType>())
1190 return CharacterExprHelper(builder, loc).readLengthFromBox(memref);
1191 fir::emitFatalError(loc, "memref has wrong type");
1192 }
1193 if (typeParams.empty()) {
1194 fir::emitFatalError(loc, "array_load must have typeparams");
1195 }
1196 if (fir::isa_char(seqTy.getEleTy())) {
1197 assert(typeParams.size() == 1 && "too many typeparams");
1198 return typeParams.front();
1199 }
1200 TODO(loc, "LEN of character must be computed at runtime");
1201 }
1202
createZeroValue(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Type type)1203 mlir::Value fir::factory::createZeroValue(fir::FirOpBuilder &builder,
1204 mlir::Location loc, mlir::Type type) {
1205 mlir::Type i1 = builder.getIntegerType(1);
1206 if (type.isa<fir::LogicalType>() || type == i1)
1207 return builder.createConvert(loc, type, builder.createBool(loc, false));
1208 if (fir::isa_integer(type))
1209 return builder.createIntegerConstant(loc, type, 0);
1210 if (fir::isa_real(type))
1211 return builder.createRealZeroConstant(loc, type);
1212 if (fir::isa_complex(type)) {
1213 fir::factory::Complex complexHelper(builder, loc);
1214 mlir::Type partType = complexHelper.getComplexPartType(type);
1215 mlir::Value zeroPart = builder.createRealZeroConstant(loc, partType);
1216 return complexHelper.createComplex(type, zeroPart, zeroPart);
1217 }
1218 fir::emitFatalError(loc, "internal: trying to generate zero value of non "
1219 "numeric or logical type");
1220 }
1221
getIntIfConstant(mlir::Value value)1222 llvm::Optional<std::int64_t> fir::factory::getIntIfConstant(mlir::Value value) {
1223 if (auto *definingOp = value.getDefiningOp())
1224 if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp))
1225 if (auto intAttr = cst.getValue().dyn_cast<mlir::IntegerAttr>())
1226 return intAttr.getInt();
1227 return {};
1228 }
1229
1230 llvm::Optional<std::int64_t>
getExtentFromTriplet(mlir::Value lb,mlir::Value ub,mlir::Value stride)1231 fir::factory::getExtentFromTriplet(mlir::Value lb, mlir::Value ub,
1232 mlir::Value stride) {
1233 std::function<llvm::Optional<std::int64_t>(mlir::Value)> getConstantValue =
1234 [&](mlir::Value value) -> llvm::Optional<std::int64_t> {
1235 if (auto valInt = fir::factory::getIntIfConstant(value))
1236 return valInt;
1237 auto *definingOp = value.getDefiningOp();
1238 if (mlir::isa_and_nonnull<fir::ConvertOp>(definingOp)) {
1239 auto valOp = mlir::dyn_cast<fir::ConvertOp>(definingOp);
1240 return getConstantValue(valOp.getValue());
1241 }
1242 return {};
1243 };
1244 if (auto lbInt = getConstantValue(lb)) {
1245 if (auto ubInt = getConstantValue(ub)) {
1246 if (auto strideInt = getConstantValue(stride)) {
1247 if (strideInt.value() != 0) {
1248 std::int64_t extent =
1249 1 + (ubInt.value() - lbInt.value()) / strideInt.value();
1250 if (extent > 0)
1251 return extent;
1252 }
1253 }
1254 }
1255 }
1256 return {};
1257 }
1258
genMaxWithZero(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Value value)1259 mlir::Value fir::factory::genMaxWithZero(fir::FirOpBuilder &builder,
1260 mlir::Location loc,
1261 mlir::Value value) {
1262 mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0);
1263 if (mlir::Operation *definingOp = value.getDefiningOp())
1264 if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp))
1265 if (auto intAttr = cst.getValue().dyn_cast<mlir::IntegerAttr>())
1266 return intAttr.getInt() > 0 ? value : zero;
1267 mlir::Value valueIsGreater = builder.create<mlir::arith::CmpIOp>(
1268 loc, mlir::arith::CmpIPredicate::sgt, value, zero);
1269 return builder.create<mlir::arith::SelectOp>(loc, valueIsGreater, value,
1270 zero);
1271 }
1272