[gn build] Port 69b8cf4f0621
[llvm-project.git] / flang / lib / Lower / ConvertConstant.cpp
blobe56fde247828b67a460138fdd3acd13e94a46f11
1 //===-- ConvertConstant.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 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
11 //===----------------------------------------------------------------------===//
13 #include "flang/Lower/ConvertConstant.h"
14 #include "flang/Evaluate/expression.h"
15 #include "flang/Lower/AbstractConverter.h"
16 #include "flang/Lower/BuiltinModules.h"
17 #include "flang/Lower/ConvertExprToHLFIR.h"
18 #include "flang/Lower/ConvertType.h"
19 #include "flang/Lower/ConvertVariable.h"
20 #include "flang/Lower/Mangler.h"
21 #include "flang/Lower/StatementContext.h"
22 #include "flang/Lower/SymbolMap.h"
23 #include "flang/Optimizer/Builder/Complex.h"
24 #include "flang/Optimizer/Builder/MutableBox.h"
25 #include "flang/Optimizer/Builder/Todo.h"
27 #include <algorithm>
29 /// Convert string, \p s, to an APFloat value. Recognize and handle Inf and
30 /// NaN strings as well. \p s is assumed to not contain any spaces.
31 static llvm::APFloat consAPFloat(const llvm::fltSemantics &fsem,
32 llvm::StringRef s) {
33 assert(!s.contains(' '));
34 if (s.compare_insensitive("-inf") == 0)
35 return llvm::APFloat::getInf(fsem, /*negative=*/true);
36 if (s.compare_insensitive("inf") == 0 || s.compare_insensitive("+inf") == 0)
37 return llvm::APFloat::getInf(fsem);
38 // TODO: Add support for quiet and signaling NaNs.
39 if (s.compare_insensitive("-nan") == 0)
40 return llvm::APFloat::getNaN(fsem, /*negative=*/true);
41 if (s.compare_insensitive("nan") == 0 || s.compare_insensitive("+nan") == 0)
42 return llvm::APFloat::getNaN(fsem);
43 return {fsem, s};
46 //===----------------------------------------------------------------------===//
47 // Fortran::lower::tryCreatingDenseGlobal implementation
48 //===----------------------------------------------------------------------===//
50 /// Generate an mlir attribute from a literal value
51 template <Fortran::common::TypeCategory TC, int KIND>
52 static mlir::Attribute convertToAttribute(
53 fir::FirOpBuilder &builder,
54 const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>> &value,
55 mlir::Type type) {
56 if constexpr (TC == Fortran::common::TypeCategory::Integer) {
57 if constexpr (KIND <= 8)
58 return builder.getIntegerAttr(type, value.ToInt64());
59 else {
60 static_assert(KIND <= 16, "integers with KIND > 16 are not supported");
61 return builder.getIntegerAttr(
62 type, llvm::APInt(KIND * 8,
63 {value.ToUInt64(), value.SHIFTR(64).ToUInt64()}));
65 } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
66 return builder.getIntegerAttr(type, value.IsTrue());
67 } else {
68 auto getFloatAttr = [&](const auto &value, mlir::Type type) {
69 std::string str = value.DumpHexadecimal();
70 auto floatVal =
71 consAPFloat(builder.getKindMap().getFloatSemantics(KIND), str);
72 return builder.getFloatAttr(type, floatVal);
75 if constexpr (TC == Fortran::common::TypeCategory::Real) {
76 return getFloatAttr(value, type);
77 } else {
78 static_assert(TC == Fortran::common::TypeCategory::Complex,
79 "type values cannot be converted to attributes");
80 mlir::Type eleTy = mlir::cast<mlir::ComplexType>(type).getElementType();
81 llvm::SmallVector<mlir::Attribute, 2> attrs = {
82 getFloatAttr(value.REAL(), eleTy),
83 getFloatAttr(value.AIMAG(), eleTy)};
84 return builder.getArrayAttr(attrs);
87 return {};
90 namespace {
91 /// Helper class to lower an array constant to a global with an MLIR dense
92 /// attribute.
93 ///
94 /// If we have an array of integer, real, complex, or logical, then we can
95 /// create a global array with the dense attribute.
96 ///
97 /// The mlir tensor type can only handle integer, real, complex, or logical.
98 /// It does not currently support nested structures.
99 class DenseGlobalBuilder {
100 public:
101 static fir::GlobalOp tryCreating(fir::FirOpBuilder &builder,
102 mlir::Location loc, mlir::Type symTy,
103 llvm::StringRef globalName,
104 mlir::StringAttr linkage, bool isConst,
105 const Fortran::lower::SomeExpr &initExpr,
106 cuf::DataAttributeAttr dataAttr) {
107 DenseGlobalBuilder globalBuilder;
108 Fortran::common::visit(
109 Fortran::common::visitors{
110 [&](const Fortran::evaluate::Expr<Fortran::evaluate::SomeLogical> &
111 x) { globalBuilder.tryConvertingToAttributes(builder, x); },
112 [&](const Fortran::evaluate::Expr<Fortran::evaluate::SomeInteger> &
113 x) { globalBuilder.tryConvertingToAttributes(builder, x); },
114 [&](const Fortran::evaluate::Expr<Fortran::evaluate::SomeReal> &x) {
115 globalBuilder.tryConvertingToAttributes(builder, x);
117 [&](const Fortran::evaluate::Expr<Fortran::evaluate::SomeComplex> &
118 x) { globalBuilder.tryConvertingToAttributes(builder, x); },
119 [](const auto &) {},
121 initExpr.u);
122 return globalBuilder.tryCreatingGlobal(builder, loc, symTy, globalName,
123 linkage, isConst, dataAttr);
126 template <Fortran::common::TypeCategory TC, int KIND>
127 static fir::GlobalOp tryCreating(
128 fir::FirOpBuilder &builder, mlir::Location loc, mlir::Type symTy,
129 llvm::StringRef globalName, mlir::StringAttr linkage, bool isConst,
130 const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
131 &constant,
132 cuf::DataAttributeAttr dataAttr) {
133 DenseGlobalBuilder globalBuilder;
134 globalBuilder.tryConvertingToAttributes(builder, constant);
135 return globalBuilder.tryCreatingGlobal(builder, loc, symTy, globalName,
136 linkage, isConst, dataAttr);
139 private:
140 DenseGlobalBuilder() = default;
142 /// Try converting an evaluate::Constant to a list of MLIR attributes.
143 template <Fortran::common::TypeCategory TC, int KIND>
144 void tryConvertingToAttributes(
145 fir::FirOpBuilder &builder,
146 const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
147 &constant) {
148 static_assert(TC != Fortran::common::TypeCategory::Character,
149 "must be numerical or logical");
150 auto attrTc = TC == Fortran::common::TypeCategory::Logical
151 ? Fortran::common::TypeCategory::Integer
152 : TC;
153 attributeElementType = Fortran::lower::getFIRType(
154 builder.getContext(), attrTc, KIND, std::nullopt);
155 for (auto element : constant.values())
156 attributes.push_back(
157 convertToAttribute<TC, KIND>(builder, element, attributeElementType));
160 /// Try converting an evaluate::Expr to a list of MLIR attributes.
161 template <typename SomeCat>
162 void tryConvertingToAttributes(fir::FirOpBuilder &builder,
163 const Fortran::evaluate::Expr<SomeCat> &expr) {
164 Fortran::common::visit(
165 [&](const auto &x) {
166 using TR = Fortran::evaluate::ResultType<decltype(x)>;
167 if (const auto *constant =
168 std::get_if<Fortran::evaluate::Constant<TR>>(&x.u))
169 tryConvertingToAttributes<TR::category, TR::kind>(builder,
170 *constant);
172 expr.u);
175 /// Create a fir::Global if MLIR attributes have been successfully created by
176 /// tryConvertingToAttributes.
177 fir::GlobalOp tryCreatingGlobal(fir::FirOpBuilder &builder,
178 mlir::Location loc, mlir::Type symTy,
179 llvm::StringRef globalName,
180 mlir::StringAttr linkage, bool isConst,
181 cuf::DataAttributeAttr dataAttr) const {
182 // Not a "trivial" intrinsic constant array, or empty array.
183 if (!attributeElementType || attributes.empty())
184 return {};
186 assert(mlir::isa<fir::SequenceType>(symTy) && "expecting an array global");
187 auto arrTy = mlir::cast<fir::SequenceType>(symTy);
188 llvm::SmallVector<int64_t> tensorShape(arrTy.getShape());
189 std::reverse(tensorShape.begin(), tensorShape.end());
190 auto tensorTy =
191 mlir::RankedTensorType::get(tensorShape, attributeElementType);
192 auto init = mlir::DenseElementsAttr::get(tensorTy, attributes);
193 return builder.createGlobal(loc, symTy, globalName, linkage, init, isConst,
194 /*isTarget=*/false, dataAttr);
197 llvm::SmallVector<mlir::Attribute> attributes;
198 mlir::Type attributeElementType;
200 } // namespace
202 fir::GlobalOp Fortran::lower::tryCreatingDenseGlobal(
203 fir::FirOpBuilder &builder, mlir::Location loc, mlir::Type symTy,
204 llvm::StringRef globalName, mlir::StringAttr linkage, bool isConst,
205 const Fortran::lower::SomeExpr &initExpr, cuf::DataAttributeAttr dataAttr) {
206 return DenseGlobalBuilder::tryCreating(builder, loc, symTy, globalName,
207 linkage, isConst, initExpr, dataAttr);
210 //===----------------------------------------------------------------------===//
211 // Fortran::lower::convertConstant
212 // Lower a constant to a fir::ExtendedValue.
213 //===----------------------------------------------------------------------===//
215 /// Generate a real constant with a value `value`.
216 template <int KIND>
217 static mlir::Value genRealConstant(fir::FirOpBuilder &builder,
218 mlir::Location loc,
219 const llvm::APFloat &value) {
220 mlir::Type fltTy = Fortran::lower::convertReal(builder.getContext(), KIND);
221 return builder.createRealConstant(loc, fltTy, value);
224 /// Convert a scalar literal constant to IR.
225 template <Fortran::common::TypeCategory TC, int KIND>
226 static mlir::Value genScalarLit(
227 fir::FirOpBuilder &builder, mlir::Location loc,
228 const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>> &value) {
229 if constexpr (TC == Fortran::common::TypeCategory::Integer ||
230 TC == Fortran::common::TypeCategory::Unsigned) {
231 // MLIR requires constants to be signless
232 mlir::Type ty = Fortran::lower::getFIRType(
233 builder.getContext(), Fortran::common::TypeCategory::Integer, KIND,
234 std::nullopt);
235 if (KIND == 16) {
236 auto bigInt = llvm::APInt(ty.getIntOrFloatBitWidth(),
237 TC == Fortran::common::TypeCategory::Unsigned
238 ? value.UnsignedDecimal()
239 : value.SignedDecimal(),
240 10);
241 return builder.create<mlir::arith::ConstantOp>(
242 loc, ty, mlir::IntegerAttr::get(ty, bigInt));
244 return builder.createIntegerConstant(loc, ty, value.ToInt64());
245 } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
246 return builder.createBool(loc, value.IsTrue());
247 } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
248 std::string str = value.DumpHexadecimal();
249 if constexpr (KIND == 2) {
250 auto floatVal = consAPFloat(llvm::APFloatBase::IEEEhalf(), str);
251 return genRealConstant<KIND>(builder, loc, floatVal);
252 } else if constexpr (KIND == 3) {
253 auto floatVal = consAPFloat(llvm::APFloatBase::BFloat(), str);
254 return genRealConstant<KIND>(builder, loc, floatVal);
255 } else if constexpr (KIND == 4) {
256 auto floatVal = consAPFloat(llvm::APFloatBase::IEEEsingle(), str);
257 return genRealConstant<KIND>(builder, loc, floatVal);
258 } else if constexpr (KIND == 10) {
259 auto floatVal = consAPFloat(llvm::APFloatBase::x87DoubleExtended(), str);
260 return genRealConstant<KIND>(builder, loc, floatVal);
261 } else if constexpr (KIND == 16) {
262 auto floatVal = consAPFloat(llvm::APFloatBase::IEEEquad(), str);
263 return genRealConstant<KIND>(builder, loc, floatVal);
264 } else {
265 // convert everything else to double
266 auto floatVal = consAPFloat(llvm::APFloatBase::IEEEdouble(), str);
267 return genRealConstant<KIND>(builder, loc, floatVal);
269 } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
270 mlir::Value real = genScalarLit<Fortran::common::TypeCategory::Real, KIND>(
271 builder, loc, value.REAL());
272 mlir::Value imag = genScalarLit<Fortran::common::TypeCategory::Real, KIND>(
273 builder, loc, value.AIMAG());
274 return fir::factory::Complex{builder, loc}.createComplex(real, imag);
275 } else /*constexpr*/ {
276 llvm_unreachable("unhandled constant");
280 /// Create fir::string_lit from a scalar character constant.
281 template <int KIND>
282 static fir::StringLitOp
283 createStringLitOp(fir::FirOpBuilder &builder, mlir::Location loc,
284 const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
285 Fortran::common::TypeCategory::Character, KIND>> &value,
286 [[maybe_unused]] int64_t len) {
287 if constexpr (KIND == 1) {
288 assert(value.size() == static_cast<std::uint64_t>(len));
289 return builder.createStringLitOp(loc, value);
290 } else {
291 using ET = typename std::decay_t<decltype(value)>::value_type;
292 fir::CharacterType type =
293 fir::CharacterType::get(builder.getContext(), KIND, len);
294 mlir::MLIRContext *context = builder.getContext();
295 std::int64_t size = static_cast<std::int64_t>(value.size());
296 mlir::ShapedType shape = mlir::RankedTensorType::get(
297 llvm::ArrayRef<std::int64_t>{size},
298 mlir::IntegerType::get(builder.getContext(), sizeof(ET) * 8));
299 auto denseAttr = mlir::DenseElementsAttr::get(
300 shape, llvm::ArrayRef<ET>{value.data(), value.size()});
301 auto denseTag = mlir::StringAttr::get(context, fir::StringLitOp::xlist());
302 mlir::NamedAttribute dataAttr(denseTag, denseAttr);
303 auto sizeTag = mlir::StringAttr::get(context, fir::StringLitOp::size());
304 mlir::NamedAttribute sizeAttr(sizeTag, builder.getI64IntegerAttr(len));
305 llvm::SmallVector<mlir::NamedAttribute> attrs = {dataAttr, sizeAttr};
306 return builder.create<fir::StringLitOp>(
307 loc, llvm::ArrayRef<mlir::Type>{type}, std::nullopt, attrs);
311 /// Convert a scalar literal CHARACTER to IR.
312 template <int KIND>
313 static mlir::Value
314 genScalarLit(fir::FirOpBuilder &builder, mlir::Location loc,
315 const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
316 Fortran::common::TypeCategory::Character, KIND>> &value,
317 int64_t len, bool outlineInReadOnlyMemory) {
318 // When in an initializer context, construct the literal op itself and do
319 // not construct another constant object in rodata.
320 if (!outlineInReadOnlyMemory)
321 return createStringLitOp<KIND>(builder, loc, value, len);
323 // Otherwise, the string is in a plain old expression so "outline" the value
324 // in read only data by hash consing it to a constant literal object.
326 // ASCII global constants are created using an mlir string attribute.
327 if constexpr (KIND == 1) {
328 return fir::getBase(fir::factory::createStringLiteral(builder, loc, value));
331 auto size = builder.getKindMap().getCharacterBitsize(KIND) / 8 * value.size();
332 llvm::StringRef strVal(reinterpret_cast<const char *>(value.c_str()), size);
333 std::string globalName = fir::factory::uniqueCGIdent(
334 KIND == 1 ? "cl"s : "cl"s + std::to_string(KIND), strVal);
335 fir::GlobalOp global = builder.getNamedGlobal(globalName);
336 fir::CharacterType type =
337 fir::CharacterType::get(builder.getContext(), KIND, len);
338 if (!global)
339 global = builder.createGlobalConstant(
340 loc, type, globalName,
341 [&](fir::FirOpBuilder &builder) {
342 fir::StringLitOp str =
343 createStringLitOp<KIND>(builder, loc, value, len);
344 builder.create<fir::HasValueOp>(loc, str);
346 builder.createLinkOnceLinkage());
347 return builder.create<fir::AddrOfOp>(loc, global.resultType(),
348 global.getSymbol());
351 // Helper to generate StructureConstructor component values.
352 static fir::ExtendedValue
353 genConstantValue(Fortran::lower::AbstractConverter &converter,
354 mlir::Location loc,
355 const Fortran::lower::SomeExpr &constantExpr);
357 static mlir::Value genStructureComponentInit(
358 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
359 const Fortran::semantics::Symbol &sym, const Fortran::lower::SomeExpr &expr,
360 mlir::Value res) {
361 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
362 fir::RecordType recTy = mlir::cast<fir::RecordType>(res.getType());
363 std::string name = converter.getRecordTypeFieldName(sym);
364 mlir::Type componentTy = recTy.getType(name);
365 auto fieldTy = fir::FieldType::get(recTy.getContext());
366 assert(componentTy && "failed to retrieve component");
367 // FIXME: type parameters must come from the derived-type-spec
368 auto field = builder.create<fir::FieldIndexOp>(
369 loc, fieldTy, name, recTy,
370 /*typeParams=*/mlir::ValueRange{} /*TODO*/);
372 if (Fortran::semantics::IsAllocatable(sym)) {
373 if (!Fortran::evaluate::IsNullPointer(expr)) {
374 fir::emitFatalError(loc, "constant structure constructor with an "
375 "allocatable component value that is not NULL");
376 } else {
377 // Handle NULL() initialization
378 mlir::Value componentValue{fir::factory::createUnallocatedBox(
379 builder, loc, componentTy, std::nullopt)};
380 componentValue = builder.createConvert(loc, componentTy, componentValue);
382 return builder.create<fir::InsertValueOp>(
383 loc, recTy, res, componentValue,
384 builder.getArrayAttr(field.getAttributes()));
388 if (Fortran::semantics::IsPointer(sym)) {
389 mlir::Value initialTarget;
390 if (Fortran::semantics::IsProcedure(sym)) {
391 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(expr))
392 initialTarget =
393 fir::factory::createNullBoxProc(builder, loc, componentTy);
394 else {
395 Fortran::lower::SymMap globalOpSymMap;
396 Fortran::lower::StatementContext stmtCtx;
397 auto box{getBase(Fortran::lower::convertExprToAddress(
398 loc, converter, expr, globalOpSymMap, stmtCtx))};
399 initialTarget = builder.createConvert(loc, componentTy, box);
401 } else
402 initialTarget = Fortran::lower::genInitialDataTarget(converter, loc,
403 componentTy, expr);
404 res = builder.create<fir::InsertValueOp>(
405 loc, recTy, res, initialTarget,
406 builder.getArrayAttr(field.getAttributes()));
407 return res;
410 if (Fortran::lower::isDerivedTypeWithLenParameters(sym))
411 TODO(loc, "component with length parameters in structure constructor");
413 // Special handling for scalar c_ptr/c_funptr constants. The array constant
414 // must fall through to genConstantValue() below.
415 if (Fortran::semantics::IsBuiltinCPtr(sym) && sym.Rank() == 0 &&
416 (Fortran::evaluate::GetLastSymbol(expr) ||
417 Fortran::evaluate::IsNullPointer(expr))) {
418 // Builtin c_ptr and c_funptr have special handling because designators
419 // and NULL() are handled as initial values for them as an extension
420 // (otherwise only c_ptr_null/c_funptr_null are allowed and these are
421 // replaced by structure constructors by semantics, so GetLastSymbol
422 // returns nothing).
424 // The Ev::Expr is an initializer that is a pointer target (e.g., 'x' or
425 // NULL()) that must be inserted into an intermediate cptr record value's
426 // address field, which ought to be an intptr_t on the target.
427 mlir::Value addr = fir::getBase(
428 Fortran::lower::genExtAddrInInitializer(converter, loc, expr));
429 if (mlir::isa<fir::BoxProcType>(addr.getType()))
430 addr = builder.create<fir::BoxAddrOp>(loc, addr);
431 assert((fir::isa_ref_type(addr.getType()) ||
432 mlir::isa<mlir::FunctionType>(addr.getType())) &&
433 "expect reference type for address field");
434 assert(fir::isa_derived(componentTy) &&
435 "expect C_PTR, C_FUNPTR to be a record");
436 auto cPtrRecTy = mlir::cast<fir::RecordType>(componentTy);
437 llvm::StringRef addrFieldName = Fortran::lower::builtin::cptrFieldName;
438 mlir::Type addrFieldTy = cPtrRecTy.getType(addrFieldName);
439 auto addrField = builder.create<fir::FieldIndexOp>(
440 loc, fieldTy, addrFieldName, componentTy,
441 /*typeParams=*/mlir::ValueRange{});
442 mlir::Value castAddr = builder.createConvert(loc, addrFieldTy, addr);
443 auto undef = builder.create<fir::UndefOp>(loc, componentTy);
444 addr = builder.create<fir::InsertValueOp>(
445 loc, componentTy, undef, castAddr,
446 builder.getArrayAttr(addrField.getAttributes()));
447 res = builder.create<fir::InsertValueOp>(
448 loc, recTy, res, addr, builder.getArrayAttr(field.getAttributes()));
449 return res;
452 mlir::Value val = fir::getBase(genConstantValue(converter, loc, expr));
453 assert(!fir::isa_ref_type(val.getType()) && "expecting a constant value");
454 mlir::Value castVal = builder.createConvert(loc, componentTy, val);
455 res = builder.create<fir::InsertValueOp>(
456 loc, recTy, res, castVal, builder.getArrayAttr(field.getAttributes()));
457 return res;
460 // Generate a StructureConstructor inlined (returns raw fir.type<T> value,
461 // not the address of a global constant).
462 static mlir::Value genInlinedStructureCtorLitImpl(
463 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
464 const Fortran::evaluate::StructureConstructor &ctor, mlir::Type type) {
465 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
466 auto recTy = mlir::cast<fir::RecordType>(type);
468 if (!converter.getLoweringOptions().getLowerToHighLevelFIR()) {
469 mlir::Value res = builder.create<fir::UndefOp>(loc, recTy);
470 for (const auto &[sym, expr] : ctor.values()) {
471 // Parent components need more work because they do not appear in the
472 // fir.rec type.
473 if (sym->test(Fortran::semantics::Symbol::Flag::ParentComp))
474 TODO(loc, "parent component in structure constructor");
475 res = genStructureComponentInit(converter, loc, sym, expr.value(), res);
477 return res;
480 auto fieldTy = fir::FieldType::get(recTy.getContext());
481 mlir::Value res{};
482 // When the first structure component values belong to some parent type PT
483 // and the next values belong to a type extension ET, a new undef for ET must
484 // be created and the previous PT value inserted into it. There may
485 // be empty parent types in between ET and PT, hence the list and while loop.
486 auto insertParentValueIntoExtension = [&](mlir::Type typeExtension) {
487 assert(res && "res must be set");
488 llvm::SmallVector<mlir::Type> parentTypes = {typeExtension};
489 while (true) {
490 fir::RecordType last = mlir::cast<fir::RecordType>(parentTypes.back());
491 mlir::Type next =
492 last.getType(0); // parent components are first in HLFIR.
493 if (next != res.getType())
494 parentTypes.push_back(next);
495 else
496 break;
498 for (mlir::Type parentType : llvm::reverse(parentTypes)) {
499 auto undef = builder.create<fir::UndefOp>(loc, parentType);
500 fir::RecordType parentRecTy = mlir::cast<fir::RecordType>(parentType);
501 auto field = builder.create<fir::FieldIndexOp>(
502 loc, fieldTy, parentRecTy.getTypeList()[0].first, parentType,
503 /*typeParams=*/mlir::ValueRange{} /*TODO*/);
504 res = builder.create<fir::InsertValueOp>(
505 loc, parentRecTy, undef, res,
506 builder.getArrayAttr(field.getAttributes()));
510 const Fortran::semantics::DerivedTypeSpec *curentType = nullptr;
511 for (const auto &[sym, expr] : ctor.values()) {
512 const Fortran::semantics::DerivedTypeSpec *componentParentType =
513 sym->owner().derivedTypeSpec();
514 assert(componentParentType && "failed to retrieve component parent type");
515 if (!res) {
516 mlir::Type parentType = converter.genType(*componentParentType);
517 curentType = componentParentType;
518 res = builder.create<fir::UndefOp>(loc, parentType);
519 } else if (*componentParentType != *curentType) {
520 mlir::Type parentType = converter.genType(*componentParentType);
521 insertParentValueIntoExtension(parentType);
522 curentType = componentParentType;
524 res = genStructureComponentInit(converter, loc, sym, expr.value(), res);
527 if (!res) // structure constructor for empty type.
528 return builder.create<fir::UndefOp>(loc, recTy);
530 // The last component may belong to a parent type.
531 if (res.getType() != recTy)
532 insertParentValueIntoExtension(recTy);
533 return res;
536 static mlir::Value genScalarLit(
537 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
538 const Fortran::evaluate::Scalar<Fortran::evaluate::SomeDerived> &value,
539 mlir::Type eleTy, bool outlineBigConstantsInReadOnlyMemory) {
540 if (!outlineBigConstantsInReadOnlyMemory)
541 return genInlinedStructureCtorLitImpl(converter, loc, value, eleTy);
542 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
543 auto expr = std::make_unique<Fortran::lower::SomeExpr>(toEvExpr(
544 Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived>(value)));
545 llvm::StringRef globalName =
546 converter.getUniqueLitName(loc, std::move(expr), eleTy);
547 fir::GlobalOp global = builder.getNamedGlobal(globalName);
548 if (!global) {
549 global = builder.createGlobalConstant(
550 loc, eleTy, globalName,
551 [&](fir::FirOpBuilder &builder) {
552 mlir::Value result =
553 genInlinedStructureCtorLitImpl(converter, loc, value, eleTy);
554 builder.create<fir::HasValueOp>(loc, result);
556 builder.createInternalLinkage());
558 return builder.create<fir::AddrOfOp>(loc, global.resultType(),
559 global.getSymbol());
562 /// Create an evaluate::Constant<T> array to a fir.array<> value
563 /// built with a chain of fir.insert or fir.insert_on_range operations.
564 /// This is intended to be called when building the body of a fir.global.
565 template <typename T>
566 static mlir::Value
567 genInlinedArrayLit(Fortran::lower::AbstractConverter &converter,
568 mlir::Location loc, mlir::Type arrayTy,
569 const Fortran::evaluate::Constant<T> &con) {
570 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
571 mlir::IndexType idxTy = builder.getIndexType();
572 Fortran::evaluate::ConstantSubscripts subscripts = con.lbounds();
573 auto createIdx = [&]() {
574 llvm::SmallVector<mlir::Attribute> idx;
575 for (size_t i = 0; i < subscripts.size(); ++i)
576 idx.push_back(
577 builder.getIntegerAttr(idxTy, subscripts[i] - con.lbounds()[i]));
578 return idx;
580 mlir::Value array = builder.create<fir::UndefOp>(loc, arrayTy);
581 if (Fortran::evaluate::GetSize(con.shape()) == 0)
582 return array;
583 if constexpr (T::category == Fortran::common::TypeCategory::Character) {
584 do {
585 mlir::Value elementVal =
586 genScalarLit<T::kind>(builder, loc, con.At(subscripts), con.LEN(),
587 /*outlineInReadOnlyMemory=*/false);
588 array = builder.create<fir::InsertValueOp>(
589 loc, arrayTy, array, elementVal, builder.getArrayAttr(createIdx()));
590 } while (con.IncrementSubscripts(subscripts));
591 } else if constexpr (T::category == Fortran::common::TypeCategory::Derived) {
592 do {
593 mlir::Type eleTy =
594 mlir::cast<fir::SequenceType>(arrayTy).getElementType();
595 mlir::Value elementVal =
596 genScalarLit(converter, loc, con.At(subscripts), eleTy,
597 /*outlineInReadOnlyMemory=*/false);
598 array = builder.create<fir::InsertValueOp>(
599 loc, arrayTy, array, elementVal, builder.getArrayAttr(createIdx()));
600 } while (con.IncrementSubscripts(subscripts));
601 } else {
602 llvm::SmallVector<mlir::Attribute> rangeStartIdx;
603 uint64_t rangeSize = 0;
604 mlir::Type eleTy = mlir::cast<fir::SequenceType>(arrayTy).getElementType();
605 do {
606 auto getElementVal = [&]() {
607 return builder.createConvert(loc, eleTy,
608 genScalarLit<T::category, T::kind>(
609 builder, loc, con.At(subscripts)));
611 Fortran::evaluate::ConstantSubscripts nextSubscripts = subscripts;
612 bool nextIsSame = con.IncrementSubscripts(nextSubscripts) &&
613 con.At(subscripts) == con.At(nextSubscripts);
614 if (!rangeSize && !nextIsSame) { // single (non-range) value
615 array = builder.create<fir::InsertValueOp>(
616 loc, arrayTy, array, getElementVal(),
617 builder.getArrayAttr(createIdx()));
618 } else if (!rangeSize) { // start a range
619 rangeStartIdx = createIdx();
620 rangeSize = 1;
621 } else if (nextIsSame) { // expand a range
622 ++rangeSize;
623 } else { // end a range
624 llvm::SmallVector<int64_t> rangeBounds;
625 llvm::SmallVector<mlir::Attribute> idx = createIdx();
626 for (size_t i = 0; i < idx.size(); ++i) {
627 rangeBounds.push_back(mlir::cast<mlir::IntegerAttr>(rangeStartIdx[i])
628 .getValue()
629 .getSExtValue());
630 rangeBounds.push_back(
631 mlir::cast<mlir::IntegerAttr>(idx[i]).getValue().getSExtValue());
633 array = builder.create<fir::InsertOnRangeOp>(
634 loc, arrayTy, array, getElementVal(),
635 builder.getIndexVectorAttr(rangeBounds));
636 rangeSize = 0;
638 } while (con.IncrementSubscripts(subscripts));
640 return array;
643 /// Convert an evaluate::Constant<T> array into a fir.ref<fir.array<>> value
644 /// that points to the storage of a fir.global in read only memory and is
645 /// initialized with the value of the constant.
646 /// This should not be called while generating the body of a fir.global.
647 template <typename T>
648 static mlir::Value
649 genOutlineArrayLit(Fortran::lower::AbstractConverter &converter,
650 mlir::Location loc, mlir::Type arrayTy,
651 const Fortran::evaluate::Constant<T> &constant) {
652 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
653 mlir::Type eleTy = mlir::cast<fir::SequenceType>(arrayTy).getElementType();
654 llvm::StringRef globalName = converter.getUniqueLitName(
655 loc, std::make_unique<Fortran::lower::SomeExpr>(toEvExpr(constant)),
656 eleTy);
657 fir::GlobalOp global = builder.getNamedGlobal(globalName);
658 if (!global) {
659 // Using a dense attribute for the initial value instead of creating an
660 // intialization body speeds up MLIR/LLVM compilation, but this is not
661 // always possible.
662 if constexpr (T::category == Fortran::common::TypeCategory::Logical ||
663 T::category == Fortran::common::TypeCategory::Integer ||
664 T::category == Fortran::common::TypeCategory::Real ||
665 T::category == Fortran::common::TypeCategory::Complex) {
666 global = DenseGlobalBuilder::tryCreating(
667 builder, loc, arrayTy, globalName, builder.createInternalLinkage(),
668 true, constant, {});
670 if (!global)
671 // If the number of elements of the array is huge, the compilation may
672 // use a lot of memory and take a very long time to complete.
673 // Empirical evidence shows that an array with 150000 elements of
674 // complex type takes roughly 30 seconds to compile and uses 4GB of RAM,
675 // on a modern machine.
676 // It would be nice to add a driver switch to control the array size
677 // after which flang should not continue to compile.
678 global = builder.createGlobalConstant(
679 loc, arrayTy, globalName,
680 [&](fir::FirOpBuilder &builder) {
681 mlir::Value result =
682 genInlinedArrayLit(converter, loc, arrayTy, constant);
683 builder.create<fir::HasValueOp>(loc, result);
685 builder.createInternalLinkage());
687 return builder.create<fir::AddrOfOp>(loc, global.resultType(),
688 global.getSymbol());
691 /// Convert an evaluate::Constant<T> array into an fir::ExtendedValue.
692 template <typename T>
693 static fir::ExtendedValue
694 genArrayLit(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
695 const Fortran::evaluate::Constant<T> &con,
696 bool outlineInReadOnlyMemory) {
697 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
698 Fortran::evaluate::ConstantSubscript size =
699 Fortran::evaluate::GetSize(con.shape());
700 if (size > std::numeric_limits<std::uint32_t>::max())
701 // llvm::SmallVector has limited size
702 TODO(loc, "Creation of very large array constants");
703 fir::SequenceType::Shape shape(con.shape().begin(), con.shape().end());
704 llvm::SmallVector<std::int64_t> typeParams;
705 if constexpr (T::category == Fortran::common::TypeCategory::Character)
706 typeParams.push_back(con.LEN());
707 mlir::Type eleTy;
708 if constexpr (T::category == Fortran::common::TypeCategory::Derived)
709 eleTy = Fortran::lower::translateDerivedTypeToFIRType(
710 converter, con.GetType().GetDerivedTypeSpec());
711 else
712 eleTy = Fortran::lower::getFIRType(builder.getContext(), T::category,
713 T::kind, typeParams);
714 auto arrayTy = fir::SequenceType::get(shape, eleTy);
715 mlir::Value array = outlineInReadOnlyMemory
716 ? genOutlineArrayLit(converter, loc, arrayTy, con)
717 : genInlinedArrayLit(converter, loc, arrayTy, con);
719 mlir::IndexType idxTy = builder.getIndexType();
720 llvm::SmallVector<mlir::Value> extents;
721 for (auto extent : shape)
722 extents.push_back(builder.createIntegerConstant(loc, idxTy, extent));
723 // Convert lower bounds if they are not all ones.
724 llvm::SmallVector<mlir::Value> lbounds;
725 if (llvm::any_of(con.lbounds(), [](auto lb) { return lb != 1; }))
726 for (auto lb : con.lbounds())
727 lbounds.push_back(builder.createIntegerConstant(loc, idxTy, lb));
729 if constexpr (T::category == Fortran::common::TypeCategory::Character) {
730 mlir::Value len = builder.createIntegerConstant(loc, idxTy, con.LEN());
731 return fir::CharArrayBoxValue{array, len, extents, lbounds};
732 } else {
733 return fir::ArrayBoxValue{array, extents, lbounds};
737 template <typename T>
738 fir::ExtendedValue Fortran::lower::ConstantBuilder<T>::gen(
739 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
740 const Fortran::evaluate::Constant<T> &constant,
741 bool outlineBigConstantsInReadOnlyMemory) {
742 if (constant.Rank() > 0)
743 return genArrayLit(converter, loc, constant,
744 outlineBigConstantsInReadOnlyMemory);
745 std::optional<Fortran::evaluate::Scalar<T>> opt = constant.GetScalarValue();
746 assert(opt.has_value() && "constant has no value");
747 if constexpr (T::category == Fortran::common::TypeCategory::Character) {
748 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
749 auto value =
750 genScalarLit<T::kind>(builder, loc, opt.value(), constant.LEN(),
751 outlineBigConstantsInReadOnlyMemory);
752 mlir::Value len = builder.createIntegerConstant(
753 loc, builder.getCharacterLengthType(), constant.LEN());
754 return fir::CharBoxValue{value, len};
755 } else if constexpr (T::category == Fortran::common::TypeCategory::Derived) {
756 mlir::Type eleTy = Fortran::lower::translateDerivedTypeToFIRType(
757 converter, opt->GetType().GetDerivedTypeSpec());
758 return genScalarLit(converter, loc, *opt, eleTy,
759 outlineBigConstantsInReadOnlyMemory);
760 } else {
761 return genScalarLit<T::category, T::kind>(converter.getFirOpBuilder(), loc,
762 opt.value());
766 static fir::ExtendedValue
767 genConstantValue(Fortran::lower::AbstractConverter &converter,
768 mlir::Location loc,
769 const Fortran::evaluate::Expr<Fortran::evaluate::SomeDerived>
770 &constantExpr) {
771 if (const auto *constant = std::get_if<
772 Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived>>(
773 &constantExpr.u))
774 return Fortran::lower::convertConstant(converter, loc, *constant,
775 /*outline=*/false);
776 if (const auto *structCtor =
777 std::get_if<Fortran::evaluate::StructureConstructor>(&constantExpr.u))
778 return Fortran::lower::genInlinedStructureCtorLit(converter, loc,
779 *structCtor);
780 fir::emitFatalError(loc, "not a constant derived type expression");
783 template <Fortran::common::TypeCategory TC, int KIND>
784 static fir::ExtendedValue genConstantValue(
785 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
786 const Fortran::evaluate::Expr<Fortran::evaluate::Type<TC, KIND>>
787 &constantExpr) {
788 using T = Fortran::evaluate::Type<TC, KIND>;
789 if (const auto *constant =
790 std::get_if<Fortran::evaluate::Constant<T>>(&constantExpr.u))
791 return Fortran::lower::convertConstant(converter, loc, *constant,
792 /*outline=*/false);
793 fir::emitFatalError(loc, "not an evaluate::Constant<T>");
796 static fir::ExtendedValue
797 genConstantValue(Fortran::lower::AbstractConverter &converter,
798 mlir::Location loc,
799 const Fortran::lower::SomeExpr &constantExpr) {
800 return Fortran::common::visit(
801 [&](const auto &x) -> fir::ExtendedValue {
802 using T = std::decay_t<decltype(x)>;
803 if constexpr (Fortran::common::HasMember<
804 T, Fortran::lower::CategoryExpression>) {
805 if constexpr (T::Result::category ==
806 Fortran::common::TypeCategory::Derived) {
807 return genConstantValue(converter, loc, x);
808 } else {
809 return Fortran::common::visit(
810 [&](const auto &preciseKind) {
811 return genConstantValue(converter, loc, preciseKind);
813 x.u);
815 } else {
816 fir::emitFatalError(loc, "unexpected typeless constant value");
819 constantExpr.u);
822 fir::ExtendedValue Fortran::lower::genInlinedStructureCtorLit(
823 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
824 const Fortran::evaluate::StructureConstructor &ctor) {
825 mlir::Type type = Fortran::lower::translateDerivedTypeToFIRType(
826 converter, ctor.derivedTypeSpec());
827 return genInlinedStructureCtorLitImpl(converter, loc, ctor, type);
830 using namespace Fortran::evaluate;
831 FOR_EACH_SPECIFIC_TYPE(template class Fortran::lower::ConstantBuilder, )