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/MutableBox.h"
13 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
14 #include "flang/Optimizer/Support/FatalError.h"
15 #include "flang/Optimizer/Support/InternalNames.h"
16 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
17 #include "llvm/ADT/StringExtras.h"
18 #include "llvm/Support/CommandLine.h"
19 #include "llvm/Support/MD5.h"
20 
21 static llvm::cl::opt<std::size_t>
22     nameLengthHashSize("length-to-hash-string-literal",
23                        llvm::cl::desc("string literals that exceed this length"
24                                       " will use a hash value as their symbol "
25                                       "name"),
26                        llvm::cl::init(32));
27 
28 mlir::FuncOp fir::FirOpBuilder::createFunction(mlir::Location loc,
29                                                mlir::ModuleOp module,
30                                                llvm::StringRef name,
31                                                mlir::FunctionType ty) {
32   return fir::createFuncOp(loc, module, name, ty);
33 }
34 
35 mlir::FuncOp fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp,
36                                                  llvm::StringRef name) {
37   return modOp.lookupSymbol<mlir::FuncOp>(name);
38 }
39 
40 fir::GlobalOp fir::FirOpBuilder::getNamedGlobal(mlir::ModuleOp modOp,
41                                                 llvm::StringRef name) {
42   return modOp.lookupSymbol<fir::GlobalOp>(name);
43 }
44 
45 mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy) {
46   assert(!eleTy.isa<fir::ReferenceType>() && "cannot be a reference type");
47   return fir::ReferenceType::get(eleTy);
48 }
49 
50 mlir::Type fir::FirOpBuilder::getVarLenSeqTy(mlir::Type eleTy, unsigned rank) {
51   fir::SequenceType::Shape shape(rank, fir::SequenceType::getUnknownExtent());
52   return fir::SequenceType::get(shape, eleTy);
53 }
54 
55 mlir::Type fir::FirOpBuilder::getRealType(int kind) {
56   switch (kindMap.getRealTypeID(kind)) {
57   case llvm::Type::TypeID::HalfTyID:
58     return mlir::FloatType::getF16(getContext());
59   case llvm::Type::TypeID::FloatTyID:
60     return mlir::FloatType::getF32(getContext());
61   case llvm::Type::TypeID::DoubleTyID:
62     return mlir::FloatType::getF64(getContext());
63   case llvm::Type::TypeID::X86_FP80TyID:
64     return mlir::FloatType::getF80(getContext());
65   case llvm::Type::TypeID::FP128TyID:
66     return mlir::FloatType::getF128(getContext());
67   default:
68     fir::emitFatalError(UnknownLoc::get(getContext()),
69                         "unsupported type !fir.real<kind>");
70   }
71 }
72 
73 mlir::Value fir::FirOpBuilder::createNullConstant(mlir::Location loc,
74                                                   mlir::Type ptrType) {
75   auto ty = ptrType ? ptrType : getRefType(getNoneType());
76   return create<fir::ZeroOp>(loc, ty);
77 }
78 
79 mlir::Value fir::FirOpBuilder::createIntegerConstant(mlir::Location loc,
80                                                      mlir::Type ty,
81                                                      std::int64_t cst) {
82   return create<mlir::ConstantOp>(loc, ty, getIntegerAttr(ty, cst));
83 }
84 
85 mlir::Value
86 fir::FirOpBuilder::createRealConstant(mlir::Location loc, mlir::Type fltTy,
87                                       llvm::APFloat::integerPart val) {
88   auto apf = [&]() -> llvm::APFloat {
89     if (auto ty = fltTy.dyn_cast<fir::RealType>())
90       return llvm::APFloat(kindMap.getFloatSemantics(ty.getFKind()), val);
91     if (fltTy.isF16())
92       return llvm::APFloat(llvm::APFloat::IEEEhalf(), val);
93     if (fltTy.isBF16())
94       return llvm::APFloat(llvm::APFloat::BFloat(), val);
95     if (fltTy.isF32())
96       return llvm::APFloat(llvm::APFloat::IEEEsingle(), val);
97     if (fltTy.isF64())
98       return llvm::APFloat(llvm::APFloat::IEEEdouble(), val);
99     if (fltTy.isF80())
100       return llvm::APFloat(llvm::APFloat::x87DoubleExtended(), val);
101     if (fltTy.isF128())
102       return llvm::APFloat(llvm::APFloat::IEEEquad(), val);
103     llvm_unreachable("unhandled MLIR floating-point type");
104   };
105   return createRealConstant(loc, fltTy, apf());
106 }
107 
108 mlir::Value fir::FirOpBuilder::createRealConstant(mlir::Location loc,
109                                                   mlir::Type fltTy,
110                                                   const llvm::APFloat &value) {
111   if (fltTy.isa<mlir::FloatType>()) {
112     auto attr = getFloatAttr(fltTy, value);
113     return create<mlir::arith::ConstantOp>(loc, fltTy, attr);
114   }
115   llvm_unreachable("should use builtin floating-point type");
116 }
117 
118 static llvm::SmallVector<mlir::Value>
119 elideExtentsAlreadyInType(mlir::Type type, mlir::ValueRange shape) {
120   auto arrTy = type.dyn_cast<fir::SequenceType>();
121   if (shape.empty() || !arrTy)
122     return {};
123   // elide the constant dimensions before construction
124   assert(shape.size() == arrTy.getDimension());
125   llvm::SmallVector<mlir::Value> dynamicShape;
126   auto typeShape = arrTy.getShape();
127   for (unsigned i = 0, end = arrTy.getDimension(); i < end; ++i)
128     if (typeShape[i] == fir::SequenceType::getUnknownExtent())
129       dynamicShape.push_back(shape[i]);
130   return dynamicShape;
131 }
132 
133 static llvm::SmallVector<mlir::Value>
134 elideLengthsAlreadyInType(mlir::Type type, mlir::ValueRange lenParams) {
135   if (lenParams.empty())
136     return {};
137   if (auto arrTy = type.dyn_cast<fir::SequenceType>())
138     type = arrTy.getEleTy();
139   if (fir::hasDynamicSize(type))
140     return lenParams;
141   return {};
142 }
143 
144 /// Allocate a local variable.
145 /// A local variable ought to have a name in the source code.
146 mlir::Value fir::FirOpBuilder::allocateLocal(
147     mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName,
148     llvm::StringRef name, bool pinned, llvm::ArrayRef<mlir::Value> shape,
149     llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) {
150   // Convert the shape extents to `index`, as needed.
151   llvm::SmallVector<mlir::Value> indices;
152   llvm::SmallVector<mlir::Value> elidedShape =
153       elideExtentsAlreadyInType(ty, shape);
154   llvm::SmallVector<mlir::Value> elidedLenParams =
155       elideLengthsAlreadyInType(ty, lenParams);
156   auto idxTy = getIndexType();
157   llvm::for_each(elidedShape, [&](mlir::Value sh) {
158     indices.push_back(createConvert(loc, idxTy, sh));
159   });
160   // Add a target attribute, if needed.
161   llvm::SmallVector<mlir::NamedAttribute> attrs;
162   if (asTarget)
163     attrs.emplace_back(
164         mlir::Identifier::get(fir::getTargetAttrName(), getContext()),
165         getUnitAttr());
166   // Create the local variable.
167   if (name.empty()) {
168     if (uniqName.empty())
169       return create<fir::AllocaOp>(loc, ty, pinned, elidedLenParams, indices,
170                                    attrs);
171     return create<fir::AllocaOp>(loc, ty, uniqName, pinned, elidedLenParams,
172                                  indices, attrs);
173   }
174   return create<fir::AllocaOp>(loc, ty, uniqName, name, pinned, elidedLenParams,
175                                indices, attrs);
176 }
177 
178 mlir::Value fir::FirOpBuilder::allocateLocal(
179     mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName,
180     llvm::StringRef name, llvm::ArrayRef<mlir::Value> shape,
181     llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) {
182   return allocateLocal(loc, ty, uniqName, name, /*pinned=*/false, shape,
183                        lenParams, asTarget);
184 }
185 
186 /// Get the block for adding Allocas.
187 mlir::Block *fir::FirOpBuilder::getAllocaBlock() {
188   // auto iface =
189   //     getRegion().getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>();
190   // return iface ? iface.getAllocaBlock() : getEntryBlock();
191   return getEntryBlock();
192 }
193 
194 /// Create a temporary variable on the stack. Anonymous temporaries have no
195 /// `name` value. Temporaries do not require a uniqued name.
196 mlir::Value
197 fir::FirOpBuilder::createTemporary(mlir::Location loc, mlir::Type type,
198                                    llvm::StringRef name, mlir::ValueRange shape,
199                                    mlir::ValueRange lenParams,
200                                    llvm::ArrayRef<mlir::NamedAttribute> attrs) {
201   llvm::SmallVector<mlir::Value> dynamicShape =
202       elideExtentsAlreadyInType(type, shape);
203   llvm::SmallVector<mlir::Value> dynamicLength =
204       elideLengthsAlreadyInType(type, lenParams);
205   InsertPoint insPt;
206   const bool hoistAlloc = dynamicShape.empty() && dynamicLength.empty();
207   if (hoistAlloc) {
208     insPt = saveInsertionPoint();
209     setInsertionPointToStart(getAllocaBlock());
210   }
211 
212   // If the alloca is inside an OpenMP Op which will be outlined then pin the
213   // alloca here.
214   const bool pinned =
215       getRegion().getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>();
216   assert(!type.isa<fir::ReferenceType>() && "cannot be a reference");
217   auto ae =
218       create<fir::AllocaOp>(loc, type, /*unique_name=*/llvm::StringRef{}, name,
219                             pinned, dynamicLength, dynamicShape, attrs);
220   if (hoistAlloc)
221     restoreInsertionPoint(insPt);
222   return ae;
223 }
224 
225 /// Create a global variable in the (read-only) data section. A global variable
226 /// must have a unique name to identify and reference it.
227 fir::GlobalOp
228 fir::FirOpBuilder::createGlobal(mlir::Location loc, mlir::Type type,
229                                 llvm::StringRef name, mlir::StringAttr linkage,
230                                 mlir::Attribute value, bool isConst) {
231   auto module = getModule();
232   auto insertPt = saveInsertionPoint();
233   if (auto glob = module.lookupSymbol<fir::GlobalOp>(name))
234     return glob;
235   setInsertionPoint(module.getBody(), module.getBody()->end());
236   auto glob = create<fir::GlobalOp>(loc, name, isConst, type, value, linkage);
237   restoreInsertionPoint(insertPt);
238   return glob;
239 }
240 
241 fir::GlobalOp fir::FirOpBuilder::createGlobal(
242     mlir::Location loc, mlir::Type type, llvm::StringRef name, bool isConst,
243     std::function<void(FirOpBuilder &)> bodyBuilder, mlir::StringAttr linkage) {
244   auto module = getModule();
245   auto insertPt = saveInsertionPoint();
246   if (auto glob = module.lookupSymbol<fir::GlobalOp>(name))
247     return glob;
248   setInsertionPoint(module.getBody(), module.getBody()->end());
249   auto glob = create<fir::GlobalOp>(loc, name, isConst, type, mlir::Attribute{},
250                                     linkage);
251   auto &region = glob.getRegion();
252   region.push_back(new mlir::Block);
253   auto &block = glob.getRegion().back();
254   setInsertionPointToStart(&block);
255   bodyBuilder(*this);
256   restoreInsertionPoint(insertPt);
257   return glob;
258 }
259 
260 mlir::Value fir::FirOpBuilder::createConvert(mlir::Location loc,
261                                              mlir::Type toTy, mlir::Value val) {
262   if (val.getType() != toTy) {
263     assert(!fir::isa_derived(toTy));
264     return create<fir::ConvertOp>(loc, toTy, val);
265   }
266   return val;
267 }
268 
269 fir::StringLitOp fir::FirOpBuilder::createStringLitOp(mlir::Location loc,
270                                                       llvm::StringRef data) {
271   auto type = fir::CharacterType::get(getContext(), 1, data.size());
272   auto strAttr = mlir::StringAttr::get(getContext(), data);
273   auto valTag = mlir::Identifier::get(fir::StringLitOp::value(), getContext());
274   mlir::NamedAttribute dataAttr(valTag, strAttr);
275   auto sizeTag = mlir::Identifier::get(fir::StringLitOp::size(), getContext());
276   mlir::NamedAttribute sizeAttr(sizeTag, getI64IntegerAttr(data.size()));
277   llvm::SmallVector<mlir::NamedAttribute> attrs{dataAttr, sizeAttr};
278   return create<fir::StringLitOp>(loc, llvm::ArrayRef<mlir::Type>{type},
279                                   llvm::None, attrs);
280 }
281 
282 mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc,
283                                         llvm::ArrayRef<mlir::Value> exts) {
284   auto shapeType = fir::ShapeType::get(getContext(), exts.size());
285   return create<fir::ShapeOp>(loc, shapeType, exts);
286 }
287 
288 mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc,
289                                         llvm::ArrayRef<mlir::Value> shift,
290                                         llvm::ArrayRef<mlir::Value> exts) {
291   auto shapeType = fir::ShapeShiftType::get(getContext(), exts.size());
292   llvm::SmallVector<mlir::Value> shapeArgs;
293   auto idxTy = getIndexType();
294   for (auto [lbnd, ext] : llvm::zip(shift, exts)) {
295     auto lb = createConvert(loc, idxTy, lbnd);
296     shapeArgs.push_back(lb);
297     shapeArgs.push_back(ext);
298   }
299   return create<fir::ShapeShiftOp>(loc, shapeType, shapeArgs);
300 }
301 
302 mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc,
303                                         const fir::AbstractArrayBox &arr) {
304   if (arr.lboundsAllOne())
305     return genShape(loc, arr.getExtents());
306   return genShape(loc, arr.getLBounds(), arr.getExtents());
307 }
308 
309 mlir::Value fir::FirOpBuilder::createShape(mlir::Location loc,
310                                            const fir::ExtendedValue &exv) {
311   return exv.match(
312       [&](const fir::ArrayBoxValue &box) { return genShape(loc, box); },
313       [&](const fir::CharArrayBoxValue &box) { return genShape(loc, box); },
314       [&](const fir::BoxValue &box) -> mlir::Value {
315         if (!box.getLBounds().empty()) {
316           auto shiftType =
317               fir::ShiftType::get(getContext(), box.getLBounds().size());
318           return create<fir::ShiftOp>(loc, shiftType, box.getLBounds());
319         }
320         return {};
321       },
322       [&](const fir::MutableBoxValue &) -> mlir::Value {
323         // MutableBoxValue must be read into another category to work with them
324         // outside of allocation/assignment contexts.
325         fir::emitFatalError(loc, "createShape on MutableBoxValue");
326       },
327       [&](auto) -> mlir::Value { fir::emitFatalError(loc, "not an array"); });
328 }
329 
330 static mlir::Value genNullPointerComparison(fir::FirOpBuilder &builder,
331                                             mlir::Location loc,
332                                             mlir::Value addr,
333                                             arith::CmpIPredicate condition) {
334   auto intPtrTy = builder.getIntPtrType();
335   auto ptrToInt = builder.createConvert(loc, intPtrTy, addr);
336   auto c0 = builder.createIntegerConstant(loc, intPtrTy, 0);
337   return builder.create<arith::CmpIOp>(loc, condition, ptrToInt, c0);
338 }
339 
340 mlir::Value fir::FirOpBuilder::genIsNotNull(mlir::Location loc,
341                                             mlir::Value addr) {
342   return genNullPointerComparison(*this, loc, addr, arith::CmpIPredicate::ne);
343 }
344 
345 mlir::Value fir::FirOpBuilder::genIsNull(mlir::Location loc, mlir::Value addr) {
346   return genNullPointerComparison(*this, loc, addr, arith::CmpIPredicate::eq);
347 }
348 
349 //===--------------------------------------------------------------------===//
350 // ExtendedValue inquiry helper implementation
351 //===--------------------------------------------------------------------===//
352 
353 mlir::Value fir::factory::readCharLen(fir::FirOpBuilder &builder,
354                                       mlir::Location loc,
355                                       const fir::ExtendedValue &box) {
356   return box.match(
357       [&](const fir::CharBoxValue &x) -> mlir::Value { return x.getLen(); },
358       [&](const fir::CharArrayBoxValue &x) -> mlir::Value {
359         return x.getLen();
360       },
361       [&](const fir::BoxValue &x) -> mlir::Value {
362         assert(x.isCharacter());
363         if (!x.getExplicitParameters().empty())
364           return x.getExplicitParameters()[0];
365         return fir::factory::CharacterExprHelper{builder, loc}
366             .readLengthFromBox(x.getAddr());
367       },
368       [&](const fir::MutableBoxValue &) -> mlir::Value {
369         // MutableBoxValue must be read into another category to work with them
370         // outside of allocation/assignment contexts.
371         fir::emitFatalError(loc, "readCharLen on MutableBoxValue");
372       },
373       [&](const auto &) -> mlir::Value {
374         fir::emitFatalError(
375             loc, "Character length inquiry on a non-character entity");
376       });
377 }
378 
379 llvm::SmallVector<mlir::Value>
380 fir::factory::readExtents(fir::FirOpBuilder &builder, mlir::Location loc,
381                           const fir::BoxValue &box) {
382   llvm::SmallVector<mlir::Value> result;
383   auto explicitExtents = box.getExplicitExtents();
384   if (!explicitExtents.empty()) {
385     result.append(explicitExtents.begin(), explicitExtents.end());
386     return result;
387   }
388   auto rank = box.rank();
389   auto idxTy = builder.getIndexType();
390   for (decltype(rank) dim = 0; dim < rank; ++dim) {
391     auto dimVal = builder.createIntegerConstant(loc, idxTy, dim);
392     auto dimInfo = builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy,
393                                                   box.getAddr(), dimVal);
394     result.emplace_back(dimInfo.getResult(1));
395   }
396   return result;
397 }
398 
399 llvm::SmallVector<mlir::Value>
400 fir::factory::getExtents(fir::FirOpBuilder &builder, mlir::Location loc,
401                          const fir::ExtendedValue &box) {
402   return box.match(
403       [&](const fir::ArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> {
404         return {x.getExtents().begin(), x.getExtents().end()};
405       },
406       [&](const fir::CharArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> {
407         return {x.getExtents().begin(), x.getExtents().end()};
408       },
409       [&](const fir::BoxValue &x) -> llvm::SmallVector<mlir::Value> {
410         return fir::factory::readExtents(builder, loc, x);
411       },
412       [&](const fir::MutableBoxValue &x) -> llvm::SmallVector<mlir::Value> {
413         auto load = fir::factory::genMutableBoxRead(builder, loc, x);
414         return fir::factory::getExtents(builder, loc, load);
415       },
416       [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; });
417 }
418 
419 std::string fir::factory::uniqueCGIdent(llvm::StringRef prefix,
420                                         llvm::StringRef name) {
421   // For "long" identifiers use a hash value
422   if (name.size() > nameLengthHashSize) {
423     llvm::MD5 hash;
424     hash.update(name);
425     llvm::MD5::MD5Result result;
426     hash.final(result);
427     llvm::SmallString<32> str;
428     llvm::MD5::stringifyResult(result, str);
429     std::string hashName = prefix.str();
430     hashName.append(".").append(str.c_str());
431     return fir::NameUniquer::doGenerated(hashName);
432   }
433   // "Short" identifiers use a reversible hex string
434   std::string nm = prefix.str();
435   return fir::NameUniquer::doGenerated(
436       nm.append(".").append(llvm::toHex(name)));
437 }
438 
439 mlir::Value fir::factory::locationToFilename(fir::FirOpBuilder &builder,
440                                              mlir::Location loc) {
441   if (auto flc = loc.dyn_cast<mlir::FileLineColLoc>()) {
442     // must be encoded as asciiz, C string
443     auto fn = flc.getFilename().str() + '\0';
444     return fir::getBase(createStringLiteral(builder, loc, fn));
445   }
446   return builder.createNullConstant(loc);
447 }
448 
449 mlir::Value fir::factory::locationToLineNo(fir::FirOpBuilder &builder,
450                                            mlir::Location loc,
451                                            mlir::Type type) {
452   if (auto flc = loc.dyn_cast<mlir::FileLineColLoc>())
453     return builder.createIntegerConstant(loc, type, flc.getLine());
454   return builder.createIntegerConstant(loc, type, 0);
455 }
456 
457 fir::ExtendedValue fir::factory::createStringLiteral(fir::FirOpBuilder &builder,
458                                                      mlir::Location loc,
459                                                      llvm::StringRef str) {
460   std::string globalName = fir::factory::uniqueCGIdent("cl", str);
461   auto type = fir::CharacterType::get(builder.getContext(), 1, str.size());
462   auto global = builder.getNamedGlobal(globalName);
463   if (!global)
464     global = builder.createGlobalConstant(
465         loc, type, globalName,
466         [&](fir::FirOpBuilder &builder) {
467           auto stringLitOp = builder.createStringLitOp(loc, str);
468           builder.create<fir::HasValueOp>(loc, stringLitOp);
469         },
470         builder.createLinkOnceLinkage());
471   auto addr = builder.create<fir::AddrOfOp>(loc, global.resultType(),
472                                             global.getSymbol());
473   auto len = builder.createIntegerConstant(
474       loc, builder.getCharacterLengthType(), str.size());
475   return fir::CharBoxValue{addr, len};
476 }
477