1 //===-- IO.cpp -- IO statement lowering -----------------------------------===//
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/
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "flang/Lower/IO.h"
14 #include "flang/Common/uint128.h"
15 #include "flang/Lower/Allocatable.h"
16 #include "flang/Lower/Bridge.h"
17 #include "flang/Lower/ConvertExpr.h"
18 #include "flang/Lower/ConvertVariable.h"
19 #include "flang/Lower/PFTBuilder.h"
20 #include "flang/Lower/Runtime.h"
21 #include "flang/Lower/StatementContext.h"
22 #include "flang/Lower/Support/Utils.h"
23 #include "flang/Lower/VectorSubscripts.h"
24 #include "flang/Optimizer/Builder/Character.h"
25 #include "flang/Optimizer/Builder/Complex.h"
26 #include "flang/Optimizer/Builder/FIRBuilder.h"
27 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
28 #include "flang/Optimizer/Builder/Todo.h"
29 #include "flang/Optimizer/Support/FIRContext.h"
30 #include "flang/Parser/parse-tree.h"
31 #include "flang/Runtime/io-api.h"
32 #include "flang/Semantics/tools.h"
33 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
34 #include "llvm/Support/Debug.h"
35
36 #define DEBUG_TYPE "flang-lower-io"
37
38 // Define additional runtime type models specific to IO.
39 namespace fir::runtime {
40 template <>
getModel()41 constexpr TypeBuilderFunc getModel<Fortran::runtime::io::IoStatementState *>() {
42 return getModel<char *>();
43 }
44 template <>
45 constexpr TypeBuilderFunc
getModel()46 getModel<const Fortran::runtime::io::NamelistGroup &>() {
47 return [](mlir::MLIRContext *context) -> mlir::Type {
48 return fir::ReferenceType::get(mlir::TupleType::get(context));
49 };
50 }
51 template <>
getModel()52 constexpr TypeBuilderFunc getModel<Fortran::runtime::io::Iostat>() {
53 return [](mlir::MLIRContext *context) -> mlir::Type {
54 return mlir::IntegerType::get(context,
55 8 * sizeof(Fortran::runtime::io::Iostat));
56 };
57 }
58 } // namespace fir::runtime
59
60 using namespace Fortran::runtime::io;
61
62 #define mkIOKey(X) FirmkKey(IONAME(X))
63
64 namespace Fortran::lower {
65 /// Static table of IO runtime calls
66 ///
67 /// This logical map contains the name and type builder function for each IO
68 /// runtime function listed in the tuple. This table is fully constructed at
69 /// compile-time. Use the `mkIOKey` macro to access the table.
70 static constexpr std::tuple<
71 mkIOKey(BeginInternalArrayListOutput), mkIOKey(BeginInternalArrayListInput),
72 mkIOKey(BeginInternalArrayFormattedOutput),
73 mkIOKey(BeginInternalArrayFormattedInput), mkIOKey(BeginInternalListOutput),
74 mkIOKey(BeginInternalListInput), mkIOKey(BeginInternalFormattedOutput),
75 mkIOKey(BeginInternalFormattedInput), mkIOKey(BeginExternalListOutput),
76 mkIOKey(BeginExternalListInput), mkIOKey(BeginExternalFormattedOutput),
77 mkIOKey(BeginExternalFormattedInput), mkIOKey(BeginUnformattedOutput),
78 mkIOKey(BeginUnformattedInput), mkIOKey(BeginWait), mkIOKey(BeginWaitAll),
79 mkIOKey(BeginClose), mkIOKey(BeginFlush), mkIOKey(BeginBackspace),
80 mkIOKey(BeginEndfile), mkIOKey(BeginRewind), mkIOKey(BeginOpenUnit),
81 mkIOKey(BeginOpenNewUnit), mkIOKey(BeginInquireUnit),
82 mkIOKey(BeginInquireFile), mkIOKey(BeginInquireIoLength),
83 mkIOKey(CheckUnitNumberInRange64), mkIOKey(CheckUnitNumberInRange128),
84 mkIOKey(EnableHandlers), mkIOKey(SetAdvance), mkIOKey(SetBlank),
85 mkIOKey(SetDecimal), mkIOKey(SetDelim), mkIOKey(SetPad), mkIOKey(SetPos),
86 mkIOKey(SetRec), mkIOKey(SetRound), mkIOKey(SetSign),
87 mkIOKey(OutputNamelist), mkIOKey(InputNamelist), mkIOKey(OutputDescriptor),
88 mkIOKey(InputDescriptor), mkIOKey(OutputUnformattedBlock),
89 mkIOKey(InputUnformattedBlock), mkIOKey(OutputInteger8),
90 mkIOKey(OutputInteger16), mkIOKey(OutputInteger32),
91 mkIOKey(OutputInteger64), mkIOKey(OutputInteger128), mkIOKey(InputInteger),
92 mkIOKey(OutputReal32), mkIOKey(InputReal32), mkIOKey(OutputReal64),
93 mkIOKey(InputReal64), mkIOKey(OutputComplex32), mkIOKey(InputComplex32),
94 mkIOKey(OutputComplex64), mkIOKey(InputComplex64), mkIOKey(OutputAscii),
95 mkIOKey(InputAscii), mkIOKey(OutputLogical), mkIOKey(InputLogical),
96 mkIOKey(SetAccess), mkIOKey(SetAction), mkIOKey(SetAsynchronous),
97 mkIOKey(SetCarriagecontrol), mkIOKey(SetEncoding), mkIOKey(SetForm),
98 mkIOKey(SetPosition), mkIOKey(SetRecl), mkIOKey(SetStatus),
99 mkIOKey(SetFile), mkIOKey(GetNewUnit), mkIOKey(GetSize),
100 mkIOKey(GetIoLength), mkIOKey(GetIoMsg), mkIOKey(InquireCharacter),
101 mkIOKey(InquireLogical), mkIOKey(InquirePendingId),
102 mkIOKey(InquireInteger64), mkIOKey(EndIoStatement)>
103 newIOTable;
104 } // namespace Fortran::lower
105
106 namespace {
107 /// IO statements may require exceptional condition handling. A statement that
108 /// encounters an exceptional condition may branch to a label given on an ERR
109 /// (error), END (end-of-file), or EOR (end-of-record) specifier. An IOSTAT
110 /// specifier variable may be set to a value that indicates some condition,
111 /// and an IOMSG specifier variable may be set to a description of a condition.
112 struct ConditionSpecInfo {
113 const Fortran::lower::SomeExpr *ioStatExpr{};
114 llvm::Optional<fir::ExtendedValue> ioMsg;
115 bool hasErr{};
116 bool hasEnd{};
117 bool hasEor{};
118 fir::IfOp bigUnitIfOp;
119
120 /// Check for any condition specifier that applies to specifier processing.
hasErrorConditionSpec__anone72d7e3d0311::ConditionSpecInfo121 bool hasErrorConditionSpec() const { return ioStatExpr != nullptr || hasErr; }
122
123 /// Check for any condition specifier that applies to data transfer items
124 /// in a PRINT, READ, WRITE, or WAIT statement. (WAIT may be irrelevant.)
hasTransferConditionSpec__anone72d7e3d0311::ConditionSpecInfo125 bool hasTransferConditionSpec() const {
126 return hasErrorConditionSpec() || hasEnd || hasEor;
127 }
128
129 /// Check for any condition specifier, including IOMSG.
hasAnyConditionSpec__anone72d7e3d0311::ConditionSpecInfo130 bool hasAnyConditionSpec() const {
131 return hasTransferConditionSpec() || ioMsg;
132 }
133 };
134 } // namespace
135
136 template <typename D>
137 static void genIoLoop(Fortran::lower::AbstractConverter &converter,
138 mlir::Value cookie, const D &ioImpliedDo,
139 bool isFormatted, bool checkResult, mlir::Value &ok,
140 bool inLoop);
141
142 /// Helper function to retrieve the name of the IO function given the key `A`
143 template <typename A>
getName()144 static constexpr const char *getName() {
145 return std::get<A>(Fortran::lower::newIOTable).name;
146 }
147
148 /// Helper function to retrieve the type model signature builder of the IO
149 /// function as defined by the key `A`
150 template <typename A>
getTypeModel()151 static constexpr fir::runtime::FuncTypeBuilderFunc getTypeModel() {
152 return std::get<A>(Fortran::lower::newIOTable).getTypeModel();
153 }
154
getLength(mlir::Type argTy)155 inline int64_t getLength(mlir::Type argTy) {
156 return argTy.cast<fir::SequenceType>().getShape()[0];
157 }
158
159 /// Get (or generate) the MLIR FuncOp for a given IO runtime function.
160 template <typename E>
getIORuntimeFunc(mlir::Location loc,fir::FirOpBuilder & builder)161 static mlir::func::FuncOp getIORuntimeFunc(mlir::Location loc,
162 fir::FirOpBuilder &builder) {
163 llvm::StringRef name = getName<E>();
164 mlir::func::FuncOp func = builder.getNamedFunction(name);
165 if (func)
166 return func;
167 auto funTy = getTypeModel<E>()(builder.getContext());
168 func = builder.createFunction(loc, name, funTy);
169 func->setAttr("fir.runtime", builder.getUnitAttr());
170 func->setAttr("fir.io", builder.getUnitAttr());
171 return func;
172 }
173
174 /// Generate calls to end an IO statement. Return the IOSTAT value, if any.
175 /// It is the caller's responsibility to generate branches on that value.
genEndIO(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,ConditionSpecInfo & csi,Fortran::lower::StatementContext & stmtCtx)176 static mlir::Value genEndIO(Fortran::lower::AbstractConverter &converter,
177 mlir::Location loc, mlir::Value cookie,
178 ConditionSpecInfo &csi,
179 Fortran::lower::StatementContext &stmtCtx) {
180 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
181 if (csi.ioMsg) {
182 mlir::func::FuncOp getIoMsg =
183 getIORuntimeFunc<mkIOKey(GetIoMsg)>(loc, builder);
184 builder.create<fir::CallOp>(
185 loc, getIoMsg,
186 mlir::ValueRange{
187 cookie,
188 builder.createConvert(loc, getIoMsg.getFunctionType().getInput(1),
189 fir::getBase(*csi.ioMsg)),
190 builder.createConvert(loc, getIoMsg.getFunctionType().getInput(2),
191 fir::getLen(*csi.ioMsg))});
192 }
193 mlir::func::FuncOp endIoStatement =
194 getIORuntimeFunc<mkIOKey(EndIoStatement)>(loc, builder);
195 auto call = builder.create<fir::CallOp>(loc, endIoStatement,
196 mlir::ValueRange{cookie});
197 mlir::Value iostat = call.getResult(0);
198 if (csi.bigUnitIfOp) {
199 stmtCtx.finalizeAndPop();
200 builder.create<fir::ResultOp>(loc, iostat);
201 builder.setInsertionPointAfter(csi.bigUnitIfOp);
202 iostat = csi.bigUnitIfOp.getResult(0);
203 }
204 if (csi.ioStatExpr) {
205 mlir::Value ioStatVar =
206 fir::getBase(converter.genExprAddr(loc, csi.ioStatExpr, stmtCtx));
207 mlir::Value ioStatResult =
208 builder.createConvert(loc, converter.genType(*csi.ioStatExpr), iostat);
209 builder.create<fir::StoreOp>(loc, ioStatResult, ioStatVar);
210 }
211 return csi.hasTransferConditionSpec() ? iostat : mlir::Value{};
212 }
213
214 /// Make the next call in the IO statement conditional on runtime result `ok`.
215 /// If a call returns `ok==false`, further suboperation calls for an IO
216 /// statement will be skipped. This may generate branch heavy, deeply nested
217 /// conditionals for IO statements with a large number of suboperations.
makeNextConditionalOn(fir::FirOpBuilder & builder,mlir::Location loc,bool checkResult,mlir::Value ok,bool inLoop=false)218 static void makeNextConditionalOn(fir::FirOpBuilder &builder,
219 mlir::Location loc, bool checkResult,
220 mlir::Value ok, bool inLoop = false) {
221 if (!checkResult || !ok)
222 // Either no IO calls need to be checked, or this will be the first call.
223 return;
224
225 // A previous IO call for a statement returned the bool `ok`. If this call
226 // is in a fir.iterate_while loop, the result must be propagated up to the
227 // loop scope as an extra ifOp result. (The propagation is done in genIoLoop.)
228 mlir::TypeRange resTy;
229 if (inLoop)
230 resTy = builder.getI1Type();
231 auto ifOp = builder.create<fir::IfOp>(loc, resTy, ok,
232 /*withElseRegion=*/inLoop);
233 builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
234 }
235
236 /// Retrieve or generate a runtime description of NAMELIST group `symbol`.
237 /// The form of the description is defined in runtime header file namelist.h.
238 /// Static descriptors are generated for global objects; local descriptors for
239 /// local objects. If all descriptors are static, the NamelistGroup is static.
240 static mlir::Value
getNamelistGroup(Fortran::lower::AbstractConverter & converter,const Fortran::semantics::Symbol & symbol,Fortran::lower::StatementContext & stmtCtx)241 getNamelistGroup(Fortran::lower::AbstractConverter &converter,
242 const Fortran::semantics::Symbol &symbol,
243 Fortran::lower::StatementContext &stmtCtx) {
244 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
245 mlir::Location loc = converter.getCurrentLocation();
246 std::string groupMangleName = converter.mangleName(symbol);
247 if (auto group = builder.getNamedGlobal(groupMangleName))
248 return builder.create<fir::AddrOfOp>(loc, group.resultType(),
249 group.getSymbol());
250
251 const auto &details =
252 symbol.GetUltimate().get<Fortran::semantics::NamelistDetails>();
253 mlir::MLIRContext *context = builder.getContext();
254 mlir::StringAttr linkOnce = builder.createLinkOnceLinkage();
255 mlir::IndexType idxTy = builder.getIndexType();
256 mlir::IntegerType sizeTy = builder.getIntegerType(8 * sizeof(std::size_t));
257 fir::ReferenceType charRefTy =
258 fir::ReferenceType::get(builder.getIntegerType(8));
259 fir::ReferenceType descRefTy =
260 fir::ReferenceType::get(fir::BoxType::get(mlir::NoneType::get(context)));
261 fir::SequenceType listTy = fir::SequenceType::get(
262 details.objects().size(),
263 mlir::TupleType::get(context, {charRefTy, descRefTy}));
264 mlir::TupleType groupTy = mlir::TupleType::get(
265 context, {charRefTy, sizeTy, fir::ReferenceType::get(listTy)});
266 auto stringAddress = [&](const Fortran::semantics::Symbol &symbol) {
267 return fir::factory::createStringLiteral(builder, loc,
268 symbol.name().ToString() + '\0');
269 };
270
271 // Define object names, and static descriptors for global objects.
272 bool groupIsLocal = false;
273 stringAddress(symbol);
274 for (const Fortran::semantics::Symbol &s : details.objects()) {
275 stringAddress(s);
276 if (!Fortran::lower::symbolIsGlobal(s)) {
277 groupIsLocal = true;
278 continue;
279 }
280 // We know we have a global item. It it's not a pointer or allocatable,
281 // create a static pointer to it.
282 if (!IsAllocatableOrPointer(s)) {
283 std::string mangleName = converter.mangleName(s) + ".desc";
284 if (builder.getNamedGlobal(mangleName))
285 continue;
286 const auto expr = Fortran::evaluate::AsGenericExpr(s);
287 fir::BoxType boxTy =
288 fir::BoxType::get(fir::PointerType::get(converter.genType(s)));
289 auto descFunc = [&](fir::FirOpBuilder &b) {
290 auto box =
291 Fortran::lower::genInitialDataTarget(converter, loc, boxTy, *expr);
292 b.create<fir::HasValueOp>(loc, box);
293 };
294 builder.createGlobalConstant(loc, boxTy, mangleName, descFunc, linkOnce);
295 }
296 }
297
298 // Define the list of Items.
299 mlir::Value listAddr =
300 groupIsLocal ? builder.create<fir::AllocaOp>(loc, listTy) : mlir::Value{};
301 std::string listMangleName = groupMangleName + ".list";
302 auto listFunc = [&](fir::FirOpBuilder &builder) {
303 mlir::Value list = builder.create<fir::UndefOp>(loc, listTy);
304 mlir::IntegerAttr zero = builder.getIntegerAttr(idxTy, 0);
305 mlir::IntegerAttr one = builder.getIntegerAttr(idxTy, 1);
306 llvm::SmallVector<mlir::Attribute, 2> idx = {mlir::Attribute{},
307 mlir::Attribute{}};
308 size_t n = 0;
309 for (const Fortran::semantics::Symbol &s : details.objects()) {
310 idx[0] = builder.getIntegerAttr(idxTy, n);
311 idx[1] = zero;
312 mlir::Value nameAddr =
313 builder.createConvert(loc, charRefTy, fir::getBase(stringAddress(s)));
314 list = builder.create<fir::InsertValueOp>(loc, listTy, list, nameAddr,
315 builder.getArrayAttr(idx));
316 idx[1] = one;
317 mlir::Value descAddr;
318 // Items that we created end in ".desc".
319 std::string suffix = IsAllocatableOrPointer(s) ? "" : ".desc";
320 if (auto desc =
321 builder.getNamedGlobal(converter.mangleName(s) + suffix)) {
322 descAddr = builder.create<fir::AddrOfOp>(loc, desc.resultType(),
323 desc.getSymbol());
324 } else {
325 const auto expr = Fortran::evaluate::AsGenericExpr(s);
326 fir::ExtendedValue exv = converter.genExprAddr(*expr, stmtCtx);
327 mlir::Type type = fir::getBase(exv).getType();
328 if (mlir::Type baseTy = fir::dyn_cast_ptrOrBoxEleTy(type))
329 type = baseTy;
330 fir::BoxType boxType = fir::BoxType::get(fir::PointerType::get(type));
331 descAddr = builder.createTemporary(loc, boxType);
332 fir::MutableBoxValue box = fir::MutableBoxValue(descAddr, {}, {});
333 fir::factory::associateMutableBox(builder, loc, box, exv,
334 /*lbounds=*/llvm::None);
335 }
336 descAddr = builder.createConvert(loc, descRefTy, descAddr);
337 list = builder.create<fir::InsertValueOp>(loc, listTy, list, descAddr,
338 builder.getArrayAttr(idx));
339 ++n;
340 }
341 if (groupIsLocal)
342 builder.create<fir::StoreOp>(loc, list, listAddr);
343 else
344 builder.create<fir::HasValueOp>(loc, list);
345 };
346 if (groupIsLocal)
347 listFunc(builder);
348 else
349 builder.createGlobalConstant(loc, listTy, listMangleName, listFunc,
350 linkOnce);
351
352 // Define the group.
353 mlir::Value groupAddr = groupIsLocal
354 ? builder.create<fir::AllocaOp>(loc, groupTy)
355 : mlir::Value{};
356 auto groupFunc = [&](fir::FirOpBuilder &builder) {
357 mlir::IntegerAttr zero = builder.getIntegerAttr(idxTy, 0);
358 mlir::IntegerAttr one = builder.getIntegerAttr(idxTy, 1);
359 mlir::IntegerAttr two = builder.getIntegerAttr(idxTy, 2);
360 mlir::Value group = builder.create<fir::UndefOp>(loc, groupTy);
361 mlir::Value nameAddr = builder.createConvert(
362 loc, charRefTy, fir::getBase(stringAddress(symbol)));
363 group = builder.create<fir::InsertValueOp>(loc, groupTy, group, nameAddr,
364 builder.getArrayAttr(zero));
365 mlir::Value itemCount =
366 builder.createIntegerConstant(loc, sizeTy, details.objects().size());
367 group = builder.create<fir::InsertValueOp>(loc, groupTy, group, itemCount,
368 builder.getArrayAttr(one));
369 if (fir::GlobalOp list = builder.getNamedGlobal(listMangleName))
370 listAddr = builder.create<fir::AddrOfOp>(loc, list.resultType(),
371 list.getSymbol());
372 assert(listAddr && "missing namelist object list");
373 group = builder.create<fir::InsertValueOp>(loc, groupTy, group, listAddr,
374 builder.getArrayAttr(two));
375 if (groupIsLocal)
376 builder.create<fir::StoreOp>(loc, group, groupAddr);
377 else
378 builder.create<fir::HasValueOp>(loc, group);
379 };
380 if (groupIsLocal) {
381 groupFunc(builder);
382 } else {
383 fir::GlobalOp group =
384 builder.createGlobal(loc, groupTy, groupMangleName,
385 /*isConst=*/true, groupFunc, linkOnce);
386 groupAddr = builder.create<fir::AddrOfOp>(loc, group.resultType(),
387 group.getSymbol());
388 }
389 assert(groupAddr && "missing namelist group result");
390 return groupAddr;
391 }
392
393 /// Generate a namelist IO call.
genNamelistIO(Fortran::lower::AbstractConverter & converter,mlir::Value cookie,mlir::func::FuncOp funcOp,Fortran::semantics::Symbol & symbol,bool checkResult,mlir::Value & ok,Fortran::lower::StatementContext & stmtCtx)394 static void genNamelistIO(Fortran::lower::AbstractConverter &converter,
395 mlir::Value cookie, mlir::func::FuncOp funcOp,
396 Fortran::semantics::Symbol &symbol, bool checkResult,
397 mlir::Value &ok,
398 Fortran::lower::StatementContext &stmtCtx) {
399 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
400 mlir::Location loc = converter.getCurrentLocation();
401 makeNextConditionalOn(builder, loc, checkResult, ok);
402 mlir::Type argType = funcOp.getFunctionType().getInput(1);
403 mlir::Value groupAddr = getNamelistGroup(converter, symbol, stmtCtx);
404 groupAddr = builder.createConvert(loc, argType, groupAddr);
405 llvm::SmallVector<mlir::Value> args = {cookie, groupAddr};
406 ok = builder.create<fir::CallOp>(loc, funcOp, args).getResult(0);
407 }
408
409 /// Get the output function to call for a value of the given type.
getOutputFunc(mlir::Location loc,fir::FirOpBuilder & builder,mlir::Type type,bool isFormatted)410 static mlir::func::FuncOp getOutputFunc(mlir::Location loc,
411 fir::FirOpBuilder &builder,
412 mlir::Type type, bool isFormatted) {
413 if (!isFormatted)
414 return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
415 if (auto ty = type.dyn_cast<mlir::IntegerType>()) {
416 switch (ty.getWidth()) {
417 case 1:
418 return getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
419 case 8:
420 return getIORuntimeFunc<mkIOKey(OutputInteger8)>(loc, builder);
421 case 16:
422 return getIORuntimeFunc<mkIOKey(OutputInteger16)>(loc, builder);
423 case 32:
424 return getIORuntimeFunc<mkIOKey(OutputInteger32)>(loc, builder);
425 case 64:
426 return getIORuntimeFunc<mkIOKey(OutputInteger64)>(loc, builder);
427 case 128:
428 return getIORuntimeFunc<mkIOKey(OutputInteger128)>(loc, builder);
429 }
430 llvm_unreachable("unknown OutputInteger kind");
431 }
432 if (auto ty = type.dyn_cast<mlir::FloatType>()) {
433 if (auto width = ty.getWidth(); width == 32)
434 return getIORuntimeFunc<mkIOKey(OutputReal32)>(loc, builder);
435 else if (width == 64)
436 return getIORuntimeFunc<mkIOKey(OutputReal64)>(loc, builder);
437 }
438 auto kindMap = fir::getKindMapping(builder.getModule());
439 if (auto ty = type.dyn_cast<fir::ComplexType>()) {
440 // COMPLEX(KIND=k) corresponds to a pair of REAL(KIND=k).
441 auto width = kindMap.getRealBitsize(ty.getFKind());
442 if (width == 32)
443 return getIORuntimeFunc<mkIOKey(OutputComplex32)>(loc, builder);
444 else if (width == 64)
445 return getIORuntimeFunc<mkIOKey(OutputComplex64)>(loc, builder);
446 }
447 if (type.isa<fir::LogicalType>())
448 return getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
449 if (fir::factory::CharacterExprHelper::isCharacterScalar(type)) {
450 // TODO: What would it mean if the default CHARACTER KIND is set to a wide
451 // character encoding scheme? How do we handle UTF-8? Is it a distinct KIND
452 // value? For now, assume that if the default CHARACTER KIND is 8 bit,
453 // then it is an ASCII string and UTF-8 is unsupported.
454 auto asciiKind = kindMap.defaultCharacterKind();
455 if (kindMap.getCharacterBitsize(asciiKind) == 8 &&
456 fir::factory::CharacterExprHelper::getCharacterKind(type) == asciiKind)
457 return getIORuntimeFunc<mkIOKey(OutputAscii)>(loc, builder);
458 }
459 return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
460 }
461
462 /// Generate a sequence of output data transfer calls.
genOutputItemList(Fortran::lower::AbstractConverter & converter,mlir::Value cookie,const std::list<Fortran::parser::OutputItem> & items,bool isFormatted,bool checkResult,mlir::Value & ok,bool inLoop)463 static void genOutputItemList(
464 Fortran::lower::AbstractConverter &converter, mlir::Value cookie,
465 const std::list<Fortran::parser::OutputItem> &items, bool isFormatted,
466 bool checkResult, mlir::Value &ok, bool inLoop) {
467 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
468 for (const Fortran::parser::OutputItem &item : items) {
469 if (const auto &impliedDo = std::get_if<1>(&item.u)) {
470 genIoLoop(converter, cookie, impliedDo->value(), isFormatted, checkResult,
471 ok, inLoop);
472 continue;
473 }
474 auto &pExpr = std::get<Fortran::parser::Expr>(item.u);
475 mlir::Location loc = converter.genLocation(pExpr.source);
476 makeNextConditionalOn(builder, loc, checkResult, ok, inLoop);
477 Fortran::lower::StatementContext stmtCtx;
478
479 const auto *expr = Fortran::semantics::GetExpr(pExpr);
480 if (!expr)
481 fir::emitFatalError(loc, "internal error: could not get evaluate::Expr");
482 mlir::Type itemTy = converter.genType(*expr);
483 mlir::func::FuncOp outputFunc =
484 getOutputFunc(loc, builder, itemTy, isFormatted);
485 mlir::Type argType = outputFunc.getFunctionType().getInput(1);
486 assert((isFormatted || argType.isa<fir::BoxType>()) &&
487 "expect descriptor for unformatted IO runtime");
488 llvm::SmallVector<mlir::Value> outputFuncArgs = {cookie};
489 fir::factory::CharacterExprHelper helper{builder, loc};
490 if (argType.isa<fir::BoxType>()) {
491 mlir::Value box = fir::getBase(converter.genExprBox(loc, *expr, stmtCtx));
492 outputFuncArgs.push_back(builder.createConvert(loc, argType, box));
493 } else if (helper.isCharacterScalar(itemTy)) {
494 fir::ExtendedValue exv = converter.genExprAddr(loc, expr, stmtCtx);
495 // scalar allocatable/pointer may also get here, not clear if
496 // genExprAddr will lower them as CharBoxValue or BoxValue.
497 if (!exv.getCharBox())
498 llvm::report_fatal_error(
499 "internal error: scalar character not in CharBox");
500 outputFuncArgs.push_back(builder.createConvert(
501 loc, outputFunc.getFunctionType().getInput(1), fir::getBase(exv)));
502 outputFuncArgs.push_back(builder.createConvert(
503 loc, outputFunc.getFunctionType().getInput(2), fir::getLen(exv)));
504 } else {
505 fir::ExtendedValue itemBox = converter.genExprValue(loc, expr, stmtCtx);
506 mlir::Value itemValue = fir::getBase(itemBox);
507 if (fir::isa_complex(itemTy)) {
508 auto parts =
509 fir::factory::Complex{builder, loc}.extractParts(itemValue);
510 outputFuncArgs.push_back(parts.first);
511 outputFuncArgs.push_back(parts.second);
512 } else {
513 itemValue = builder.createConvert(loc, argType, itemValue);
514 outputFuncArgs.push_back(itemValue);
515 }
516 }
517 ok = builder.create<fir::CallOp>(loc, outputFunc, outputFuncArgs)
518 .getResult(0);
519 }
520 }
521
522 /// Get the input function to call for a value of the given type.
getInputFunc(mlir::Location loc,fir::FirOpBuilder & builder,mlir::Type type,bool isFormatted)523 static mlir::func::FuncOp getInputFunc(mlir::Location loc,
524 fir::FirOpBuilder &builder,
525 mlir::Type type, bool isFormatted) {
526 if (!isFormatted)
527 return getIORuntimeFunc<mkIOKey(InputDescriptor)>(loc, builder);
528 if (auto ty = type.dyn_cast<mlir::IntegerType>())
529 return ty.getWidth() == 1
530 ? getIORuntimeFunc<mkIOKey(InputLogical)>(loc, builder)
531 : getIORuntimeFunc<mkIOKey(InputInteger)>(loc, builder);
532 if (auto ty = type.dyn_cast<mlir::FloatType>()) {
533 if (auto width = ty.getWidth(); width == 32)
534 return getIORuntimeFunc<mkIOKey(InputReal32)>(loc, builder);
535 else if (width == 64)
536 return getIORuntimeFunc<mkIOKey(InputReal64)>(loc, builder);
537 }
538 auto kindMap = fir::getKindMapping(builder.getModule());
539 if (auto ty = type.dyn_cast<fir::ComplexType>()) {
540 auto width = kindMap.getRealBitsize(ty.getFKind());
541 if (width == 32)
542 return getIORuntimeFunc<mkIOKey(InputComplex32)>(loc, builder);
543 else if (width == 64)
544 return getIORuntimeFunc<mkIOKey(InputComplex64)>(loc, builder);
545 }
546 if (type.isa<fir::LogicalType>())
547 return getIORuntimeFunc<mkIOKey(InputLogical)>(loc, builder);
548 if (fir::factory::CharacterExprHelper::isCharacterScalar(type)) {
549 auto asciiKind = kindMap.defaultCharacterKind();
550 if (kindMap.getCharacterBitsize(asciiKind) == 8 &&
551 fir::factory::CharacterExprHelper::getCharacterKind(type) == asciiKind)
552 return getIORuntimeFunc<mkIOKey(InputAscii)>(loc, builder);
553 }
554 return getIORuntimeFunc<mkIOKey(InputDescriptor)>(loc, builder);
555 }
556
557 /// Interpret the lowest byte of a LOGICAL and store that value into the full
558 /// storage of the LOGICAL. The load, convert, and store effectively (sign or
559 /// zero) extends the lowest byte into the full LOGICAL value storage, as the
560 /// runtime is unaware of the LOGICAL value's actual bit width (it was passed
561 /// as a `bool&` to the runtime in order to be set).
boolRefToLogical(mlir::Location loc,fir::FirOpBuilder & builder,mlir::Value addr)562 static void boolRefToLogical(mlir::Location loc, fir::FirOpBuilder &builder,
563 mlir::Value addr) {
564 auto boolType = builder.getRefType(builder.getI1Type());
565 auto boolAddr = builder.createConvert(loc, boolType, addr);
566 auto boolValue = builder.create<fir::LoadOp>(loc, boolAddr);
567 auto logicalType = fir::unwrapPassByRefType(addr.getType());
568 // The convert avoid making any assumptions about how LOGICALs are actually
569 // represented (it might end-up being either a signed or zero extension).
570 auto logicalValue = builder.createConvert(loc, logicalType, boolValue);
571 builder.create<fir::StoreOp>(loc, logicalValue, addr);
572 }
573
createIoRuntimeCallForItem(mlir::Location loc,fir::FirOpBuilder & builder,mlir::func::FuncOp inputFunc,mlir::Value cookie,const fir::ExtendedValue & item)574 static mlir::Value createIoRuntimeCallForItem(mlir::Location loc,
575 fir::FirOpBuilder &builder,
576 mlir::func::FuncOp inputFunc,
577 mlir::Value cookie,
578 const fir::ExtendedValue &item) {
579 mlir::Type argType = inputFunc.getFunctionType().getInput(1);
580 llvm::SmallVector<mlir::Value> inputFuncArgs = {cookie};
581 if (argType.isa<fir::BoxType>()) {
582 mlir::Value box = fir::getBase(item);
583 assert(box.getType().isa<fir::BoxType>() && "must be previously emboxed");
584 inputFuncArgs.push_back(builder.createConvert(loc, argType, box));
585 } else {
586 mlir::Value itemAddr = fir::getBase(item);
587 mlir::Type itemTy = fir::unwrapPassByRefType(itemAddr.getType());
588 inputFuncArgs.push_back(builder.createConvert(loc, argType, itemAddr));
589 fir::factory::CharacterExprHelper charHelper{builder, loc};
590 if (charHelper.isCharacterScalar(itemTy)) {
591 mlir::Value len = fir::getLen(item);
592 inputFuncArgs.push_back(builder.createConvert(
593 loc, inputFunc.getFunctionType().getInput(2), len));
594 } else if (itemTy.isa<mlir::IntegerType>()) {
595 inputFuncArgs.push_back(builder.create<mlir::arith::ConstantOp>(
596 loc, builder.getI32IntegerAttr(
597 itemTy.cast<mlir::IntegerType>().getWidth() / 8)));
598 }
599 }
600 auto call = builder.create<fir::CallOp>(loc, inputFunc, inputFuncArgs);
601 auto itemAddr = fir::getBase(item);
602 auto itemTy = fir::unwrapRefType(itemAddr.getType());
603 if (itemTy.isa<fir::LogicalType>())
604 boolRefToLogical(loc, builder, itemAddr);
605 return call.getResult(0);
606 }
607
608 /// Generate a sequence of input data transfer calls.
genInputItemList(Fortran::lower::AbstractConverter & converter,mlir::Value cookie,const std::list<Fortran::parser::InputItem> & items,bool isFormatted,bool checkResult,mlir::Value & ok,bool inLoop)609 static void genInputItemList(Fortran::lower::AbstractConverter &converter,
610 mlir::Value cookie,
611 const std::list<Fortran::parser::InputItem> &items,
612 bool isFormatted, bool checkResult,
613 mlir::Value &ok, bool inLoop) {
614 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
615 for (const Fortran::parser::InputItem &item : items) {
616 if (const auto &impliedDo = std::get_if<1>(&item.u)) {
617 genIoLoop(converter, cookie, impliedDo->value(), isFormatted, checkResult,
618 ok, inLoop);
619 continue;
620 }
621 auto &pVar = std::get<Fortran::parser::Variable>(item.u);
622 mlir::Location loc = converter.genLocation(pVar.GetSource());
623 makeNextConditionalOn(builder, loc, checkResult, ok, inLoop);
624 Fortran::lower::StatementContext stmtCtx;
625 const auto *expr = Fortran::semantics::GetExpr(pVar);
626 if (!expr)
627 fir::emitFatalError(loc, "internal error: could not get evaluate::Expr");
628 if (Fortran::evaluate::HasVectorSubscript(*expr)) {
629 auto vectorSubscriptBox =
630 Fortran::lower::genVectorSubscriptBox(loc, converter, stmtCtx, *expr);
631 mlir::func::FuncOp inputFunc = getInputFunc(
632 loc, builder, vectorSubscriptBox.getElementType(), isFormatted);
633 const bool mustBox =
634 inputFunc.getFunctionType().getInput(1).isa<fir::BoxType>();
635 if (!checkResult) {
636 auto elementalGenerator = [&](const fir::ExtendedValue &element) {
637 createIoRuntimeCallForItem(loc, builder, inputFunc, cookie,
638 mustBox ? builder.createBox(loc, element)
639 : element);
640 };
641 vectorSubscriptBox.loopOverElements(builder, loc, elementalGenerator);
642 } else {
643 auto elementalGenerator =
644 [&](const fir::ExtendedValue &element) -> mlir::Value {
645 return createIoRuntimeCallForItem(
646 loc, builder, inputFunc, cookie,
647 mustBox ? builder.createBox(loc, element) : element);
648 };
649 if (!ok)
650 ok = builder.createBool(loc, true);
651 ok = vectorSubscriptBox.loopOverElementsWhile(builder, loc,
652 elementalGenerator, ok);
653 }
654 continue;
655 }
656 mlir::Type itemTy = converter.genType(*expr);
657 mlir::func::FuncOp inputFunc =
658 getInputFunc(loc, builder, itemTy, isFormatted);
659 auto itemExv = inputFunc.getFunctionType().getInput(1).isa<fir::BoxType>()
660 ? converter.genExprBox(loc, *expr, stmtCtx)
661 : converter.genExprAddr(loc, expr, stmtCtx);
662 ok = createIoRuntimeCallForItem(loc, builder, inputFunc, cookie, itemExv);
663 }
664 }
665
666 /// Generate an io-implied-do loop.
667 template <typename D>
genIoLoop(Fortran::lower::AbstractConverter & converter,mlir::Value cookie,const D & ioImpliedDo,bool isFormatted,bool checkResult,mlir::Value & ok,bool inLoop)668 static void genIoLoop(Fortran::lower::AbstractConverter &converter,
669 mlir::Value cookie, const D &ioImpliedDo,
670 bool isFormatted, bool checkResult, mlir::Value &ok,
671 bool inLoop) {
672 Fortran::lower::StatementContext stmtCtx;
673 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
674 mlir::Location loc = converter.getCurrentLocation();
675 makeNextConditionalOn(builder, loc, checkResult, ok, inLoop);
676 const auto &itemList = std::get<0>(ioImpliedDo.t);
677 const auto &control = std::get<1>(ioImpliedDo.t);
678 const auto &loopSym = *control.name.thing.thing.symbol;
679 mlir::Value loopVar = fir::getBase(converter.genExprAddr(
680 Fortran::evaluate::AsGenericExpr(loopSym).value(), stmtCtx));
681 auto genControlValue = [&](const Fortran::parser::ScalarIntExpr &expr) {
682 mlir::Value v = fir::getBase(
683 converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx));
684 return builder.createConvert(loc, builder.getIndexType(), v);
685 };
686 mlir::Value lowerValue = genControlValue(control.lower);
687 mlir::Value upperValue = genControlValue(control.upper);
688 mlir::Value stepValue =
689 control.step.has_value()
690 ? genControlValue(*control.step)
691 : builder.create<mlir::arith::ConstantIndexOp>(loc, 1);
692 auto genItemList = [&](const D &ioImpliedDo) {
693 if constexpr (std::is_same_v<D, Fortran::parser::InputImpliedDo>)
694 genInputItemList(converter, cookie, itemList, isFormatted, checkResult,
695 ok, /*inLoop=*/true);
696 else
697 genOutputItemList(converter, cookie, itemList, isFormatted, checkResult,
698 ok, /*inLoop=*/true);
699 };
700 if (!checkResult) {
701 // No IO call result checks - the loop is a fir.do_loop op.
702 auto doLoopOp = builder.create<fir::DoLoopOp>(
703 loc, lowerValue, upperValue, stepValue, /*unordered=*/false,
704 /*finalCountValue=*/true);
705 builder.setInsertionPointToStart(doLoopOp.getBody());
706 mlir::Value lcv = builder.createConvert(
707 loc, fir::unwrapRefType(loopVar.getType()), doLoopOp.getInductionVar());
708 builder.create<fir::StoreOp>(loc, lcv, loopVar);
709 genItemList(ioImpliedDo);
710 builder.setInsertionPointToEnd(doLoopOp.getBody());
711 mlir::Value result = builder.create<mlir::arith::AddIOp>(
712 loc, doLoopOp.getInductionVar(), doLoopOp.getStep());
713 builder.create<fir::ResultOp>(loc, result);
714 builder.setInsertionPointAfter(doLoopOp);
715 // The loop control variable may be used after the loop.
716 lcv = builder.createConvert(loc, fir::unwrapRefType(loopVar.getType()),
717 doLoopOp.getResult(0));
718 builder.create<fir::StoreOp>(loc, lcv, loopVar);
719 return;
720 }
721 // Check IO call results - the loop is a fir.iterate_while op.
722 if (!ok)
723 ok = builder.createBool(loc, true);
724 auto iterWhileOp = builder.create<fir::IterWhileOp>(
725 loc, lowerValue, upperValue, stepValue, ok, /*finalCountValue*/ true);
726 builder.setInsertionPointToStart(iterWhileOp.getBody());
727 mlir::Value lcv =
728 builder.createConvert(loc, fir::unwrapRefType(loopVar.getType()),
729 iterWhileOp.getInductionVar());
730 builder.create<fir::StoreOp>(loc, lcv, loopVar);
731 ok = iterWhileOp.getIterateVar();
732 mlir::Value falseValue =
733 builder.createIntegerConstant(loc, builder.getI1Type(), 0);
734 genItemList(ioImpliedDo);
735 // Unwind nested IO call scopes, filling in true and false ResultOp's.
736 for (mlir::Operation *op = builder.getBlock()->getParentOp();
737 mlir::isa<fir::IfOp>(op); op = op->getBlock()->getParentOp()) {
738 auto ifOp = mlir::dyn_cast<fir::IfOp>(op);
739 mlir::Operation *lastOp = &ifOp.getThenRegion().front().back();
740 builder.setInsertionPointAfter(lastOp);
741 // The primary ifOp result is the result of an IO call or loop.
742 if (mlir::isa<fir::CallOp, fir::IfOp>(*lastOp))
743 builder.create<fir::ResultOp>(loc, lastOp->getResult(0));
744 else
745 builder.create<fir::ResultOp>(loc, ok); // loop result
746 // The else branch propagates an early exit false result.
747 builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
748 builder.create<fir::ResultOp>(loc, falseValue);
749 }
750 builder.setInsertionPointToEnd(iterWhileOp.getBody());
751 mlir::OpResult iterateResult = builder.getBlock()->back().getResult(0);
752 mlir::Value inductionResult0 = iterWhileOp.getInductionVar();
753 auto inductionResult1 = builder.create<mlir::arith::AddIOp>(
754 loc, inductionResult0, iterWhileOp.getStep());
755 auto inductionResult = builder.create<mlir::arith::SelectOp>(
756 loc, iterateResult, inductionResult1, inductionResult0);
757 llvm::SmallVector<mlir::Value> results = {inductionResult, iterateResult};
758 builder.create<fir::ResultOp>(loc, results);
759 ok = iterWhileOp.getResult(1);
760 builder.setInsertionPointAfter(iterWhileOp);
761 // The loop control variable may be used after the loop.
762 lcv = builder.createConvert(loc, fir::unwrapRefType(loopVar.getType()),
763 iterWhileOp.getResult(0));
764 builder.create<fir::StoreOp>(loc, lcv, loopVar);
765 }
766
767 //===----------------------------------------------------------------------===//
768 // Default argument generation.
769 //===----------------------------------------------------------------------===//
770
locToFilename(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Type toType)771 static mlir::Value locToFilename(Fortran::lower::AbstractConverter &converter,
772 mlir::Location loc, mlir::Type toType) {
773 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
774 return builder.createConvert(loc, toType,
775 fir::factory::locationToFilename(builder, loc));
776 }
777
locToLineNo(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Type toType)778 static mlir::Value locToLineNo(Fortran::lower::AbstractConverter &converter,
779 mlir::Location loc, mlir::Type toType) {
780 return fir::factory::locationToLineNo(converter.getFirOpBuilder(), loc,
781 toType);
782 }
783
getDefaultScratch(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Type toType)784 static mlir::Value getDefaultScratch(fir::FirOpBuilder &builder,
785 mlir::Location loc, mlir::Type toType) {
786 mlir::Value null = builder.create<mlir::arith::ConstantOp>(
787 loc, builder.getI64IntegerAttr(0));
788 return builder.createConvert(loc, toType, null);
789 }
790
getDefaultScratchLen(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Type toType)791 static mlir::Value getDefaultScratchLen(fir::FirOpBuilder &builder,
792 mlir::Location loc, mlir::Type toType) {
793 return builder.create<mlir::arith::ConstantOp>(
794 loc, builder.getIntegerAttr(toType, 0));
795 }
796
797 /// Generate a reference to a buffer and the length of buffer given
798 /// a character expression. An array expression will be cast to scalar
799 /// character as long as they are contiguous.
800 static std::tuple<mlir::Value, mlir::Value>
genBuffer(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::lower::SomeExpr & expr,mlir::Type strTy,mlir::Type lenTy,Fortran::lower::StatementContext & stmtCtx)801 genBuffer(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
802 const Fortran::lower::SomeExpr &expr, mlir::Type strTy,
803 mlir::Type lenTy, Fortran::lower::StatementContext &stmtCtx) {
804 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
805 fir::ExtendedValue exprAddr = converter.genExprAddr(expr, stmtCtx);
806 fir::factory::CharacterExprHelper helper(builder, loc);
807 using ValuePair = std::pair<mlir::Value, mlir::Value>;
808 auto [buff, len] = exprAddr.match(
809 [&](const fir::CharBoxValue &x) -> ValuePair {
810 return {x.getBuffer(), x.getLen()};
811 },
812 [&](const fir::CharArrayBoxValue &x) -> ValuePair {
813 fir::CharBoxValue scalar = helper.toScalarCharacter(x);
814 return {scalar.getBuffer(), scalar.getLen()};
815 },
816 [&](const fir::BoxValue &) -> ValuePair {
817 // May need to copy before after IO to handle contiguous
818 // aspect. Not sure descriptor can get here though.
819 TODO(loc, "character descriptor to contiguous buffer");
820 },
821 [&](const auto &) -> ValuePair {
822 llvm::report_fatal_error(
823 "internal error: IO buffer is not a character");
824 });
825 buff = builder.createConvert(loc, strTy, buff);
826 len = builder.createConvert(loc, lenTy, len);
827 return {buff, len};
828 }
829
830 /// Lower a string literal. Many arguments to the runtime are conveyed as
831 /// Fortran CHARACTER literals.
832 template <typename A>
833 static std::tuple<mlir::Value, mlir::Value, mlir::Value>
lowerStringLit(Fortran::lower::AbstractConverter & converter,mlir::Location loc,Fortran::lower::StatementContext & stmtCtx,const A & syntax,mlir::Type strTy,mlir::Type lenTy,mlir::Type ty2={})834 lowerStringLit(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
835 Fortran::lower::StatementContext &stmtCtx, const A &syntax,
836 mlir::Type strTy, mlir::Type lenTy, mlir::Type ty2 = {}) {
837 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
838 auto *expr = Fortran::semantics::GetExpr(syntax);
839 if (!expr)
840 fir::emitFatalError(loc, "internal error: null semantic expr in IO");
841 auto [buff, len] = genBuffer(converter, loc, *expr, strTy, lenTy, stmtCtx);
842 mlir::Value kind;
843 if (ty2) {
844 auto kindVal = expr->GetType().value().kind();
845 kind = builder.create<mlir::arith::ConstantOp>(
846 loc, builder.getIntegerAttr(ty2, kindVal));
847 }
848 return {buff, len, kind};
849 }
850
851 /// Pass the body of the FORMAT statement in as if it were a CHARACTER literal
852 /// constant. NB: This is the prescribed manner in which the front-end passes
853 /// this information to lowering.
854 static std::tuple<mlir::Value, mlir::Value, mlir::Value>
lowerSourceTextAsStringLit(Fortran::lower::AbstractConverter & converter,mlir::Location loc,llvm::StringRef text,mlir::Type strTy,mlir::Type lenTy)855 lowerSourceTextAsStringLit(Fortran::lower::AbstractConverter &converter,
856 mlir::Location loc, llvm::StringRef text,
857 mlir::Type strTy, mlir::Type lenTy) {
858 text = text.drop_front(text.find('('));
859 text = text.take_front(text.rfind(')') + 1);
860 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
861 mlir::Value addrGlobalStringLit =
862 fir::getBase(fir::factory::createStringLiteral(builder, loc, text));
863 mlir::Value buff = builder.createConvert(loc, strTy, addrGlobalStringLit);
864 mlir::Value len = builder.createIntegerConstant(loc, lenTy, text.size());
865 return {buff, len, mlir::Value{}};
866 }
867
868 //===----------------------------------------------------------------------===//
869 // Handle IO statement specifiers.
870 // These are threaded together for a single statement via the passed cookie.
871 //===----------------------------------------------------------------------===//
872
873 /// Generic to build an integral argument to the runtime.
874 template <typename A, typename B>
genIntIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const B & spec)875 mlir::Value genIntIOOption(Fortran::lower::AbstractConverter &converter,
876 mlir::Location loc, mlir::Value cookie,
877 const B &spec) {
878 Fortran::lower::StatementContext localStatementCtx;
879 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
880 mlir::func::FuncOp ioFunc = getIORuntimeFunc<A>(loc, builder);
881 mlir::FunctionType ioFuncTy = ioFunc.getFunctionType();
882 mlir::Value expr = fir::getBase(converter.genExprValue(
883 loc, Fortran::semantics::GetExpr(spec.v), localStatementCtx));
884 mlir::Value val = builder.createConvert(loc, ioFuncTy.getInput(1), expr);
885 llvm::SmallVector<mlir::Value> ioArgs = {cookie, val};
886 return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
887 }
888
889 /// Generic to build a string argument to the runtime. This passes a CHARACTER
890 /// as a pointer to the buffer and a LEN parameter.
891 template <typename A, typename B>
genCharIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const B & spec)892 mlir::Value genCharIOOption(Fortran::lower::AbstractConverter &converter,
893 mlir::Location loc, mlir::Value cookie,
894 const B &spec) {
895 Fortran::lower::StatementContext localStatementCtx;
896 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
897 mlir::func::FuncOp ioFunc = getIORuntimeFunc<A>(loc, builder);
898 mlir::FunctionType ioFuncTy = ioFunc.getFunctionType();
899 std::tuple<mlir::Value, mlir::Value, mlir::Value> tup =
900 lowerStringLit(converter, loc, localStatementCtx, spec,
901 ioFuncTy.getInput(1), ioFuncTy.getInput(2));
902 llvm::SmallVector<mlir::Value> ioArgs = {cookie, std::get<0>(tup),
903 std::get<1>(tup)};
904 return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
905 }
906
907 template <typename A>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const A & spec)908 mlir::Value genIOOption(Fortran::lower::AbstractConverter &converter,
909 mlir::Location loc, mlir::Value cookie, const A &spec) {
910 // These specifiers are processed in advance elsewhere - skip them here.
911 using PreprocessedSpecs =
912 std::tuple<Fortran::parser::EndLabel, Fortran::parser::EorLabel,
913 Fortran::parser::ErrLabel, Fortran::parser::FileUnitNumber,
914 Fortran::parser::Format, Fortran::parser::IoUnit,
915 Fortran::parser::MsgVariable, Fortran::parser::Name,
916 Fortran::parser::StatVariable>;
917 static_assert(Fortran::common::HasMember<A, PreprocessedSpecs>,
918 "missing genIOOPtion specialization");
919 return {};
920 }
921
922 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::FileNameExpr & spec)923 mlir::Value genIOOption<Fortran::parser::FileNameExpr>(
924 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
925 mlir::Value cookie, const Fortran::parser::FileNameExpr &spec) {
926 Fortran::lower::StatementContext localStatementCtx;
927 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
928 // has an extra KIND argument
929 mlir::func::FuncOp ioFunc = getIORuntimeFunc<mkIOKey(SetFile)>(loc, builder);
930 mlir::FunctionType ioFuncTy = ioFunc.getFunctionType();
931 std::tuple<mlir::Value, mlir::Value, mlir::Value> tup =
932 lowerStringLit(converter, loc, localStatementCtx, spec,
933 ioFuncTy.getInput(1), ioFuncTy.getInput(2));
934 llvm::SmallVector<mlir::Value> ioArgs{cookie, std::get<0>(tup),
935 std::get<1>(tup)};
936 return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
937 }
938
939 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::ConnectSpec::CharExpr & spec)940 mlir::Value genIOOption<Fortran::parser::ConnectSpec::CharExpr>(
941 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
942 mlir::Value cookie, const Fortran::parser::ConnectSpec::CharExpr &spec) {
943 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
944 mlir::func::FuncOp ioFunc;
945 switch (std::get<Fortran::parser::ConnectSpec::CharExpr::Kind>(spec.t)) {
946 case Fortran::parser::ConnectSpec::CharExpr::Kind::Access:
947 ioFunc = getIORuntimeFunc<mkIOKey(SetAccess)>(loc, builder);
948 break;
949 case Fortran::parser::ConnectSpec::CharExpr::Kind::Action:
950 ioFunc = getIORuntimeFunc<mkIOKey(SetAction)>(loc, builder);
951 break;
952 case Fortran::parser::ConnectSpec::CharExpr::Kind::Asynchronous:
953 ioFunc = getIORuntimeFunc<mkIOKey(SetAsynchronous)>(loc, builder);
954 break;
955 case Fortran::parser::ConnectSpec::CharExpr::Kind::Blank:
956 ioFunc = getIORuntimeFunc<mkIOKey(SetBlank)>(loc, builder);
957 break;
958 case Fortran::parser::ConnectSpec::CharExpr::Kind::Decimal:
959 ioFunc = getIORuntimeFunc<mkIOKey(SetDecimal)>(loc, builder);
960 break;
961 case Fortran::parser::ConnectSpec::CharExpr::Kind::Delim:
962 ioFunc = getIORuntimeFunc<mkIOKey(SetDelim)>(loc, builder);
963 break;
964 case Fortran::parser::ConnectSpec::CharExpr::Kind::Encoding:
965 ioFunc = getIORuntimeFunc<mkIOKey(SetEncoding)>(loc, builder);
966 break;
967 case Fortran::parser::ConnectSpec::CharExpr::Kind::Form:
968 ioFunc = getIORuntimeFunc<mkIOKey(SetForm)>(loc, builder);
969 break;
970 case Fortran::parser::ConnectSpec::CharExpr::Kind::Pad:
971 ioFunc = getIORuntimeFunc<mkIOKey(SetPad)>(loc, builder);
972 break;
973 case Fortran::parser::ConnectSpec::CharExpr::Kind::Position:
974 ioFunc = getIORuntimeFunc<mkIOKey(SetPosition)>(loc, builder);
975 break;
976 case Fortran::parser::ConnectSpec::CharExpr::Kind::Round:
977 ioFunc = getIORuntimeFunc<mkIOKey(SetRound)>(loc, builder);
978 break;
979 case Fortran::parser::ConnectSpec::CharExpr::Kind::Sign:
980 ioFunc = getIORuntimeFunc<mkIOKey(SetSign)>(loc, builder);
981 break;
982 case Fortran::parser::ConnectSpec::CharExpr::Kind::Carriagecontrol:
983 ioFunc = getIORuntimeFunc<mkIOKey(SetCarriagecontrol)>(loc, builder);
984 break;
985 case Fortran::parser::ConnectSpec::CharExpr::Kind::Convert:
986 TODO(loc, "CONVERT not part of the runtime::io interface");
987 case Fortran::parser::ConnectSpec::CharExpr::Kind::Dispose:
988 TODO(loc, "DISPOSE not part of the runtime::io interface");
989 }
990 Fortran::lower::StatementContext localStatementCtx;
991 mlir::FunctionType ioFuncTy = ioFunc.getFunctionType();
992 std::tuple<mlir::Value, mlir::Value, mlir::Value> tup =
993 lowerStringLit(converter, loc, localStatementCtx,
994 std::get<Fortran::parser::ScalarDefaultCharExpr>(spec.t),
995 ioFuncTy.getInput(1), ioFuncTy.getInput(2));
996 llvm::SmallVector<mlir::Value> ioArgs = {cookie, std::get<0>(tup),
997 std::get<1>(tup)};
998 return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
999 }
1000
1001 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::ConnectSpec::Recl & spec)1002 mlir::Value genIOOption<Fortran::parser::ConnectSpec::Recl>(
1003 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1004 mlir::Value cookie, const Fortran::parser::ConnectSpec::Recl &spec) {
1005 return genIntIOOption<mkIOKey(SetRecl)>(converter, loc, cookie, spec);
1006 }
1007
1008 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::StatusExpr & spec)1009 mlir::Value genIOOption<Fortran::parser::StatusExpr>(
1010 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1011 mlir::Value cookie, const Fortran::parser::StatusExpr &spec) {
1012 return genCharIOOption<mkIOKey(SetStatus)>(converter, loc, cookie, spec.v);
1013 }
1014
1015 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IoControlSpec::CharExpr & spec)1016 mlir::Value genIOOption<Fortran::parser::IoControlSpec::CharExpr>(
1017 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1018 mlir::Value cookie, const Fortran::parser::IoControlSpec::CharExpr &spec) {
1019 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1020 mlir::func::FuncOp ioFunc;
1021 switch (std::get<Fortran::parser::IoControlSpec::CharExpr::Kind>(spec.t)) {
1022 case Fortran::parser::IoControlSpec::CharExpr::Kind::Advance:
1023 ioFunc = getIORuntimeFunc<mkIOKey(SetAdvance)>(loc, builder);
1024 break;
1025 case Fortran::parser::IoControlSpec::CharExpr::Kind::Blank:
1026 ioFunc = getIORuntimeFunc<mkIOKey(SetBlank)>(loc, builder);
1027 break;
1028 case Fortran::parser::IoControlSpec::CharExpr::Kind::Decimal:
1029 ioFunc = getIORuntimeFunc<mkIOKey(SetDecimal)>(loc, builder);
1030 break;
1031 case Fortran::parser::IoControlSpec::CharExpr::Kind::Delim:
1032 ioFunc = getIORuntimeFunc<mkIOKey(SetDelim)>(loc, builder);
1033 break;
1034 case Fortran::parser::IoControlSpec::CharExpr::Kind::Pad:
1035 ioFunc = getIORuntimeFunc<mkIOKey(SetPad)>(loc, builder);
1036 break;
1037 case Fortran::parser::IoControlSpec::CharExpr::Kind::Round:
1038 ioFunc = getIORuntimeFunc<mkIOKey(SetRound)>(loc, builder);
1039 break;
1040 case Fortran::parser::IoControlSpec::CharExpr::Kind::Sign:
1041 ioFunc = getIORuntimeFunc<mkIOKey(SetSign)>(loc, builder);
1042 break;
1043 }
1044 Fortran::lower::StatementContext localStatementCtx;
1045 mlir::FunctionType ioFuncTy = ioFunc.getFunctionType();
1046 std::tuple<mlir::Value, mlir::Value, mlir::Value> tup =
1047 lowerStringLit(converter, loc, localStatementCtx,
1048 std::get<Fortran::parser::ScalarDefaultCharExpr>(spec.t),
1049 ioFuncTy.getInput(1), ioFuncTy.getInput(2));
1050 llvm::SmallVector<mlir::Value> ioArgs = {cookie, std::get<0>(tup),
1051 std::get<1>(tup)};
1052 return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
1053 }
1054
1055 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IoControlSpec::Asynchronous & spec)1056 mlir::Value genIOOption<Fortran::parser::IoControlSpec::Asynchronous>(
1057 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1058 mlir::Value cookie,
1059 const Fortran::parser::IoControlSpec::Asynchronous &spec) {
1060 return genCharIOOption<mkIOKey(SetAsynchronous)>(converter, loc, cookie,
1061 spec.v);
1062 }
1063
1064 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IdVariable & spec)1065 mlir::Value genIOOption<Fortran::parser::IdVariable>(
1066 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1067 mlir::Value cookie, const Fortran::parser::IdVariable &spec) {
1068 TODO(loc, "asynchronous ID not implemented");
1069 }
1070
1071 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IoControlSpec::Pos & spec)1072 mlir::Value genIOOption<Fortran::parser::IoControlSpec::Pos>(
1073 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1074 mlir::Value cookie, const Fortran::parser::IoControlSpec::Pos &spec) {
1075 return genIntIOOption<mkIOKey(SetPos)>(converter, loc, cookie, spec);
1076 }
1077
1078 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IoControlSpec::Rec & spec)1079 mlir::Value genIOOption<Fortran::parser::IoControlSpec::Rec>(
1080 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1081 mlir::Value cookie, const Fortran::parser::IoControlSpec::Rec &spec) {
1082 return genIntIOOption<mkIOKey(SetRec)>(converter, loc, cookie, spec);
1083 }
1084
1085 /// Generate runtime call to query the read size after an input statement if
1086 /// the statement has SIZE control-spec.
1087 template <typename A>
genIOReadSize(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const A & specList,bool checkResult)1088 static void genIOReadSize(Fortran::lower::AbstractConverter &converter,
1089 mlir::Location loc, mlir::Value cookie,
1090 const A &specList, bool checkResult) {
1091 // This call is not conditional on the current IO status (ok) because the size
1092 // needs to be filled even if some error condition (end-of-file...) was met
1093 // during the input statement (in which case the runtime may return zero for
1094 // the size read).
1095 for (const auto &spec : specList)
1096 if (const auto *size =
1097 std::get_if<Fortran::parser::IoControlSpec::Size>(&spec.u)) {
1098
1099 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1100 mlir::func::FuncOp ioFunc =
1101 getIORuntimeFunc<mkIOKey(GetSize)>(loc, builder);
1102 auto sizeValue =
1103 builder.create<fir::CallOp>(loc, ioFunc, mlir::ValueRange{cookie})
1104 .getResult(0);
1105 Fortran::lower::StatementContext localStatementCtx;
1106 fir::ExtendedValue var = converter.genExprAddr(
1107 loc, Fortran::semantics::GetExpr(size->v), localStatementCtx);
1108 mlir::Value varAddr = fir::getBase(var);
1109 mlir::Type varType = fir::unwrapPassByRefType(varAddr.getType());
1110 mlir::Value sizeCast = builder.createConvert(loc, varType, sizeValue);
1111 builder.create<fir::StoreOp>(loc, sizeCast, varAddr);
1112 break;
1113 }
1114 }
1115
1116 //===----------------------------------------------------------------------===//
1117 // Gather IO statement condition specifier information (if any).
1118 //===----------------------------------------------------------------------===//
1119
1120 template <typename SEEK, typename A>
hasX(const A & list)1121 static bool hasX(const A &list) {
1122 for (const auto &spec : list)
1123 if (std::holds_alternative<SEEK>(spec.u))
1124 return true;
1125 return false;
1126 }
1127
1128 template <typename SEEK, typename A>
hasSpec(const A & stmt)1129 static bool hasSpec(const A &stmt) {
1130 return hasX<SEEK>(stmt.v);
1131 }
1132
1133 /// Get the sought expression from the specifier list.
1134 template <typename SEEK, typename A>
getExpr(const A & stmt)1135 static const Fortran::lower::SomeExpr *getExpr(const A &stmt) {
1136 for (const auto &spec : stmt.v)
1137 if (auto *f = std::get_if<SEEK>(&spec.u))
1138 return Fortran::semantics::GetExpr(f->v);
1139 llvm::report_fatal_error("must have a file unit");
1140 }
1141
1142 /// For each specifier, build the appropriate call, threading the cookie.
1143 template <typename A>
threadSpecs(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const A & specList,bool checkResult,mlir::Value & ok)1144 static void threadSpecs(Fortran::lower::AbstractConverter &converter,
1145 mlir::Location loc, mlir::Value cookie,
1146 const A &specList, bool checkResult, mlir::Value &ok) {
1147 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1148 for (const auto &spec : specList) {
1149 makeNextConditionalOn(builder, loc, checkResult, ok);
1150 ok = std::visit(
1151 Fortran::common::visitors{
1152 [&](const Fortran::parser::IoControlSpec::Size &x) -> mlir::Value {
1153 // Size must be queried after the related READ runtime calls, not
1154 // before.
1155 return ok;
1156 },
1157 [&](const Fortran::parser::ConnectSpec::Newunit &x) -> mlir::Value {
1158 // Newunit must be queried after OPEN specifier runtime calls
1159 // that may fail to avoid modifying the newunit variable if
1160 // there is an error.
1161 return ok;
1162 },
1163 [&](const auto &x) {
1164 return genIOOption(converter, loc, cookie, x);
1165 }},
1166 spec.u);
1167 }
1168 }
1169
1170 /// Most IO statements allow one or more of five optional exception condition
1171 /// handling specifiers: ERR, EOR, END, IOSTAT, and IOMSG. The first three
1172 /// cause control flow to transfer to another statement. The final two return
1173 /// information from the runtime, via a variable, about the nature of the
1174 /// condition that occurred. These condition specifiers are handled here.
1175 template <typename A>
lowerErrorSpec(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & specList)1176 ConditionSpecInfo lowerErrorSpec(Fortran::lower::AbstractConverter &converter,
1177 mlir::Location loc, const A &specList) {
1178 ConditionSpecInfo csi;
1179 const Fortran::lower::SomeExpr *ioMsgExpr = nullptr;
1180 for (const auto &spec : specList) {
1181 std::visit(
1182 Fortran::common::visitors{
1183 [&](const Fortran::parser::StatVariable &var) {
1184 csi.ioStatExpr = Fortran::semantics::GetExpr(var);
1185 },
1186 [&](const Fortran::parser::InquireSpec::IntVar &var) {
1187 if (std::get<Fortran::parser::InquireSpec::IntVar::Kind>(var.t) ==
1188 Fortran::parser::InquireSpec::IntVar::Kind::Iostat)
1189 csi.ioStatExpr = Fortran::semantics::GetExpr(
1190 std::get<Fortran::parser::ScalarIntVariable>(var.t));
1191 },
1192 [&](const Fortran::parser::MsgVariable &var) {
1193 ioMsgExpr = Fortran::semantics::GetExpr(var);
1194 },
1195 [&](const Fortran::parser::InquireSpec::CharVar &var) {
1196 if (std::get<Fortran::parser::InquireSpec::CharVar::Kind>(
1197 var.t) ==
1198 Fortran::parser::InquireSpec::CharVar::Kind::Iomsg)
1199 ioMsgExpr = Fortran::semantics::GetExpr(
1200 std::get<Fortran::parser::ScalarDefaultCharVariable>(
1201 var.t));
1202 },
1203 [&](const Fortran::parser::EndLabel &) { csi.hasEnd = true; },
1204 [&](const Fortran::parser::EorLabel &) { csi.hasEor = true; },
1205 [&](const Fortran::parser::ErrLabel &) { csi.hasErr = true; },
1206 [](const auto &) {}},
1207 spec.u);
1208 }
1209 if (ioMsgExpr) {
1210 // iomsg is a variable, its evaluation may require temps, but it cannot
1211 // itself be a temp, and it is ok to us a local statement context here.
1212 Fortran::lower::StatementContext stmtCtx;
1213 csi.ioMsg = converter.genExprAddr(loc, ioMsgExpr, stmtCtx);
1214 }
1215
1216 return csi;
1217 }
1218 template <typename A>
1219 static void
genConditionHandlerCall(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const A & specList,ConditionSpecInfo & csi)1220 genConditionHandlerCall(Fortran::lower::AbstractConverter &converter,
1221 mlir::Location loc, mlir::Value cookie,
1222 const A &specList, ConditionSpecInfo &csi) {
1223 if (!csi.hasAnyConditionSpec())
1224 return;
1225 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1226 mlir::func::FuncOp enableHandlers =
1227 getIORuntimeFunc<mkIOKey(EnableHandlers)>(loc, builder);
1228 mlir::Type boolType = enableHandlers.getFunctionType().getInput(1);
1229 auto boolValue = [&](bool specifierIsPresent) {
1230 return builder.create<mlir::arith::ConstantOp>(
1231 loc, builder.getIntegerAttr(boolType, specifierIsPresent));
1232 };
1233 llvm::SmallVector<mlir::Value> ioArgs = {cookie,
1234 boolValue(csi.ioStatExpr != nullptr),
1235 boolValue(csi.hasErr),
1236 boolValue(csi.hasEnd),
1237 boolValue(csi.hasEor),
1238 boolValue(csi.ioMsg.has_value())};
1239 builder.create<fir::CallOp>(loc, enableHandlers, ioArgs);
1240 }
1241
1242 //===----------------------------------------------------------------------===//
1243 // Data transfer helpers
1244 //===----------------------------------------------------------------------===//
1245
1246 template <typename SEEK, typename A>
hasIOControl(const A & stmt)1247 static bool hasIOControl(const A &stmt) {
1248 return hasX<SEEK>(stmt.controls);
1249 }
1250
1251 template <typename SEEK, typename A>
getIOControl(const A & stmt)1252 static const auto *getIOControl(const A &stmt) {
1253 for (const auto &spec : stmt.controls)
1254 if (const auto *result = std::get_if<SEEK>(&spec.u))
1255 return result;
1256 return static_cast<const SEEK *>(nullptr);
1257 }
1258
1259 /// Returns true iff the expression in the parse tree is not really a format but
1260 /// rather a namelist group.
1261 template <typename A>
formatIsActuallyNamelist(const A & format)1262 static bool formatIsActuallyNamelist(const A &format) {
1263 if (auto *e = std::get_if<Fortran::parser::Expr>(&format.u)) {
1264 auto *expr = Fortran::semantics::GetExpr(*e);
1265 if (const Fortran::semantics::Symbol *y =
1266 Fortran::evaluate::UnwrapWholeSymbolDataRef(*expr))
1267 return y->has<Fortran::semantics::NamelistDetails>();
1268 }
1269 return false;
1270 }
1271
1272 template <typename A>
isDataTransferFormatted(const A & stmt)1273 static bool isDataTransferFormatted(const A &stmt) {
1274 if (stmt.format)
1275 return !formatIsActuallyNamelist(*stmt.format);
1276 return hasIOControl<Fortran::parser::Format>(stmt);
1277 }
1278 template <>
isDataTransferFormatted(const Fortran::parser::PrintStmt &)1279 constexpr bool isDataTransferFormatted<Fortran::parser::PrintStmt>(
1280 const Fortran::parser::PrintStmt &) {
1281 return true; // PRINT is always formatted
1282 }
1283
1284 template <typename A>
isDataTransferList(const A & stmt)1285 static bool isDataTransferList(const A &stmt) {
1286 if (stmt.format)
1287 return std::holds_alternative<Fortran::parser::Star>(stmt.format->u);
1288 if (auto *mem = getIOControl<Fortran::parser::Format>(stmt))
1289 return std::holds_alternative<Fortran::parser::Star>(mem->u);
1290 return false;
1291 }
1292 template <>
isDataTransferList(const Fortran::parser::PrintStmt & stmt)1293 bool isDataTransferList<Fortran::parser::PrintStmt>(
1294 const Fortran::parser::PrintStmt &stmt) {
1295 return std::holds_alternative<Fortran::parser::Star>(
1296 std::get<Fortran::parser::Format>(stmt.t).u);
1297 }
1298
1299 template <typename A>
isDataTransferInternal(const A & stmt)1300 static bool isDataTransferInternal(const A &stmt) {
1301 if (stmt.iounit.has_value())
1302 return std::holds_alternative<Fortran::parser::Variable>(stmt.iounit->u);
1303 if (auto *unit = getIOControl<Fortran::parser::IoUnit>(stmt))
1304 return std::holds_alternative<Fortran::parser::Variable>(unit->u);
1305 return false;
1306 }
1307 template <>
isDataTransferInternal(const Fortran::parser::PrintStmt &)1308 constexpr bool isDataTransferInternal<Fortran::parser::PrintStmt>(
1309 const Fortran::parser::PrintStmt &) {
1310 return false;
1311 }
1312
1313 /// If the variable `var` is an array or of a KIND other than the default
1314 /// (normally 1), then a descriptor is required by the runtime IO API. This
1315 /// condition holds even in F77 sources.
getVariableBufferRequiredDescriptor(Fortran::lower::AbstractConverter & converter,const Fortran::parser::Variable & var,Fortran::lower::StatementContext & stmtCtx)1316 static llvm::Optional<fir::ExtendedValue> getVariableBufferRequiredDescriptor(
1317 Fortran::lower::AbstractConverter &converter,
1318 const Fortran::parser::Variable &var,
1319 Fortran::lower::StatementContext &stmtCtx) {
1320 fir::ExtendedValue varBox =
1321 converter.genExprAddr(var.typedExpr->v.value(), stmtCtx);
1322 fir::KindTy defCharKind = converter.getKindMap().defaultCharacterKind();
1323 mlir::Value varAddr = fir::getBase(varBox);
1324 if (fir::factory::CharacterExprHelper::getCharacterOrSequenceKind(
1325 varAddr.getType()) != defCharKind)
1326 return varBox;
1327 if (fir::factory::CharacterExprHelper::isArray(varAddr.getType()))
1328 return varBox;
1329 return llvm::None;
1330 }
1331
1332 template <typename A>
1333 static llvm::Optional<fir::ExtendedValue>
maybeGetInternalIODescriptor(Fortran::lower::AbstractConverter & converter,const A & stmt,Fortran::lower::StatementContext & stmtCtx)1334 maybeGetInternalIODescriptor(Fortran::lower::AbstractConverter &converter,
1335 const A &stmt,
1336 Fortran::lower::StatementContext &stmtCtx) {
1337 if (stmt.iounit.has_value())
1338 if (auto *var = std::get_if<Fortran::parser::Variable>(&stmt.iounit->u))
1339 return getVariableBufferRequiredDescriptor(converter, *var, stmtCtx);
1340 if (auto *unit = getIOControl<Fortran::parser::IoUnit>(stmt))
1341 if (auto *var = std::get_if<Fortran::parser::Variable>(&unit->u))
1342 return getVariableBufferRequiredDescriptor(converter, *var, stmtCtx);
1343 return llvm::None;
1344 }
1345 template <>
1346 inline llvm::Optional<fir::ExtendedValue>
maybeGetInternalIODescriptor(Fortran::lower::AbstractConverter &,const Fortran::parser::PrintStmt &,Fortran::lower::StatementContext &)1347 maybeGetInternalIODescriptor<Fortran::parser::PrintStmt>(
1348 Fortran::lower::AbstractConverter &, const Fortran::parser::PrintStmt &,
1349 Fortran::lower::StatementContext &) {
1350 return llvm::None;
1351 }
1352
1353 template <typename A>
isDataTransferAsynchronous(mlir::Location loc,const A & stmt)1354 static bool isDataTransferAsynchronous(mlir::Location loc, const A &stmt) {
1355 if (auto *asynch =
1356 getIOControl<Fortran::parser::IoControlSpec::Asynchronous>(stmt)) {
1357 // FIXME: should contain a string of YES or NO
1358 TODO(loc, "asynchronous transfers not implemented in runtime");
1359 }
1360 return false;
1361 }
1362 template <>
isDataTransferAsynchronous(mlir::Location,const Fortran::parser::PrintStmt &)1363 bool isDataTransferAsynchronous<Fortran::parser::PrintStmt>(
1364 mlir::Location, const Fortran::parser::PrintStmt &) {
1365 return false;
1366 }
1367
1368 template <typename A>
isDataTransferNamelist(const A & stmt)1369 static bool isDataTransferNamelist(const A &stmt) {
1370 if (stmt.format)
1371 return formatIsActuallyNamelist(*stmt.format);
1372 return hasIOControl<Fortran::parser::Name>(stmt);
1373 }
1374 template <>
isDataTransferNamelist(const Fortran::parser::PrintStmt &)1375 constexpr bool isDataTransferNamelist<Fortran::parser::PrintStmt>(
1376 const Fortran::parser::PrintStmt &) {
1377 return false;
1378 }
1379
1380 /// Lowers a format statment that uses an assigned variable label reference as
1381 /// a select operation to allow for run-time selection of the format statement.
1382 static std::tuple<mlir::Value, mlir::Value, mlir::Value>
lowerReferenceAsStringSelect(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::lower::SomeExpr & expr,mlir::Type strTy,mlir::Type lenTy,Fortran::lower::StatementContext & stmtCtx)1383 lowerReferenceAsStringSelect(Fortran::lower::AbstractConverter &converter,
1384 mlir::Location loc,
1385 const Fortran::lower::SomeExpr &expr,
1386 mlir::Type strTy, mlir::Type lenTy,
1387 Fortran::lower::StatementContext &stmtCtx) {
1388 // Possible optimization TODO: Instead of inlining a selectOp every time there
1389 // is a variable reference to a format statement, a function with the selectOp
1390 // could be generated to reduce code size. It is not clear if such an
1391 // optimization would be deployed very often or improve the object code
1392 // beyond, say, what GVN/GCM might produce.
1393
1394 // Create the requisite blocks to inline a selectOp.
1395 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1396 mlir::Block *startBlock = builder.getBlock();
1397 mlir::Block *endBlock = startBlock->splitBlock(builder.getInsertionPoint());
1398 mlir::Block *block = startBlock->splitBlock(builder.getInsertionPoint());
1399 builder.setInsertionPointToEnd(block);
1400
1401 llvm::SmallVector<int64_t> indexList;
1402 llvm::SmallVector<mlir::Block *> blockList;
1403
1404 auto symbol = GetLastSymbol(&expr);
1405 Fortran::lower::pft::LabelSet labels;
1406 [[maybe_unused]] auto foundLabelSet =
1407 converter.lookupLabelSet(*symbol, labels);
1408 assert(foundLabelSet && "Label not found in map");
1409
1410 for (auto label : labels) {
1411 indexList.push_back(label);
1412 auto *eval = converter.lookupLabel(label);
1413 assert(eval && "Label is missing from the table");
1414
1415 llvm::StringRef text = toStringRef(eval->position);
1416 mlir::Value stringRef;
1417 mlir::Value stringLen;
1418 if (eval->isA<Fortran::parser::FormatStmt>()) {
1419 assert(text.find('(') != llvm::StringRef::npos &&
1420 "FORMAT is unexpectedly ill-formed");
1421 // This is a format statement, so extract the spec from the text.
1422 std::tuple<mlir::Value, mlir::Value, mlir::Value> stringLit =
1423 lowerSourceTextAsStringLit(converter, loc, text, strTy, lenTy);
1424 stringRef = std::get<0>(stringLit);
1425 stringLen = std::get<1>(stringLit);
1426 } else {
1427 // This is not a format statement, so use null.
1428 stringRef = builder.createConvert(
1429 loc, strTy,
1430 builder.createIntegerConstant(loc, builder.getIndexType(), 0));
1431 stringLen = builder.createIntegerConstant(loc, lenTy, 0);
1432 }
1433
1434 // Pass the format string reference and the string length out of the select
1435 // statement.
1436 llvm::SmallVector<mlir::Value> args = {stringRef, stringLen};
1437 builder.create<mlir::cf::BranchOp>(loc, endBlock, args);
1438
1439 // Add block to the list of cases and make a new one.
1440 blockList.push_back(block);
1441 block = block->splitBlock(builder.getInsertionPoint());
1442 builder.setInsertionPointToEnd(block);
1443 }
1444
1445 // Create the unit case which should result in an error.
1446 auto *unitBlock = block->splitBlock(builder.getInsertionPoint());
1447 builder.setInsertionPointToEnd(unitBlock);
1448
1449 // Crash the program.
1450 builder.create<fir::UnreachableOp>(loc);
1451
1452 // Add unit case to the select statement.
1453 blockList.push_back(unitBlock);
1454
1455 // Lower the selectOp.
1456 builder.setInsertionPointToEnd(startBlock);
1457 auto label = fir::getBase(converter.genExprValue(loc, &expr, stmtCtx));
1458 builder.create<fir::SelectOp>(loc, label, indexList, blockList);
1459
1460 builder.setInsertionPointToEnd(endBlock);
1461 endBlock->addArgument(strTy, loc);
1462 endBlock->addArgument(lenTy, loc);
1463
1464 // Handle and return the string reference and length selected by the selectOp.
1465 auto buff = endBlock->getArgument(0);
1466 auto len = endBlock->getArgument(1);
1467
1468 return {buff, len, mlir::Value{}};
1469 }
1470
1471 /// Generate a reference to a format string. There are four cases - a format
1472 /// statement label, a character format expression, an integer that holds the
1473 /// label of a format statement, and the * case. The first three are done here.
1474 /// The * case is done elsewhere.
1475 static std::tuple<mlir::Value, mlir::Value, mlir::Value>
genFormat(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::parser::Format & format,mlir::Type strTy,mlir::Type lenTy,Fortran::lower::StatementContext & stmtCtx)1476 genFormat(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1477 const Fortran::parser::Format &format, mlir::Type strTy,
1478 mlir::Type lenTy, Fortran::lower::StatementContext &stmtCtx) {
1479 if (const auto *label = std::get_if<Fortran::parser::Label>(&format.u)) {
1480 // format statement label
1481 auto eval = converter.lookupLabel(*label);
1482 assert(eval && "FORMAT not found in PROCEDURE");
1483 return lowerSourceTextAsStringLit(
1484 converter, loc, toStringRef(eval->position), strTy, lenTy);
1485 }
1486 const auto *pExpr = std::get_if<Fortran::parser::Expr>(&format.u);
1487 assert(pExpr && "missing format expression");
1488 auto e = Fortran::semantics::GetExpr(*pExpr);
1489 if (Fortran::semantics::ExprHasTypeCategory(
1490 *e, Fortran::common::TypeCategory::Character))
1491 // character expression
1492 return lowerStringLit(converter, loc, stmtCtx, *pExpr, strTy, lenTy);
1493
1494 if (Fortran::semantics::ExprHasTypeCategory(
1495 *e, Fortran::common::TypeCategory::Integer) &&
1496 e->Rank() == 0 && Fortran::evaluate::UnwrapWholeSymbolDataRef(*e)) {
1497 // Treat as a scalar integer variable containing an ASSIGN label.
1498 return lowerReferenceAsStringSelect(converter, loc, *e, strTy, lenTy,
1499 stmtCtx);
1500 }
1501
1502 // Legacy extension: it is possible that `*e` is not a scalar INTEGER
1503 // variable containing a label value. The output appears to be the source text
1504 // that initialized the variable? Needs more investigatation.
1505 TODO(loc, "io-control-spec contains a reference to a non-integer, "
1506 "non-scalar, or non-variable");
1507 }
1508
1509 template <typename A>
1510 std::tuple<mlir::Value, mlir::Value, mlir::Value>
getFormat(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & stmt,mlir::Type strTy,mlir::Type lenTy,Fortran::lower::StatementContext & stmtCtx)1511 getFormat(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1512 const A &stmt, mlir::Type strTy, mlir::Type lenTy,
1513 Fortran ::lower::StatementContext &stmtCtx) {
1514 if (stmt.format && !formatIsActuallyNamelist(*stmt.format))
1515 return genFormat(converter, loc, *stmt.format, strTy, lenTy, stmtCtx);
1516 return genFormat(converter, loc, *getIOControl<Fortran::parser::Format>(stmt),
1517 strTy, lenTy, stmtCtx);
1518 }
1519 template <>
1520 std::tuple<mlir::Value, mlir::Value, mlir::Value>
getFormat(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::parser::PrintStmt & stmt,mlir::Type strTy,mlir::Type lenTy,Fortran::lower::StatementContext & stmtCtx)1521 getFormat<Fortran::parser::PrintStmt>(
1522 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1523 const Fortran::parser::PrintStmt &stmt, mlir::Type strTy, mlir::Type lenTy,
1524 Fortran::lower::StatementContext &stmtCtx) {
1525 return genFormat(converter, loc, std::get<Fortran::parser::Format>(stmt.t),
1526 strTy, lenTy, stmtCtx);
1527 }
1528
1529 /// Get a buffer for an internal file data transfer.
1530 template <typename A>
1531 std::tuple<mlir::Value, mlir::Value>
getBuffer(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & stmt,mlir::Type strTy,mlir::Type lenTy,Fortran::lower::StatementContext & stmtCtx)1532 getBuffer(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1533 const A &stmt, mlir::Type strTy, mlir::Type lenTy,
1534 Fortran::lower::StatementContext &stmtCtx) {
1535 const Fortran::parser::IoUnit *iounit =
1536 stmt.iounit ? &*stmt.iounit : getIOControl<Fortran::parser::IoUnit>(stmt);
1537 if (iounit)
1538 if (auto *var = std::get_if<Fortran::parser::Variable>(&iounit->u))
1539 if (auto *expr = Fortran::semantics::GetExpr(*var))
1540 return genBuffer(converter, loc, *expr, strTy, lenTy, stmtCtx);
1541 llvm::report_fatal_error("failed to get IoUnit expr");
1542 }
1543
genIOUnitNumber(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::lower::SomeExpr * iounit,mlir::Type ty,ConditionSpecInfo & csi,Fortran::lower::StatementContext & stmtCtx)1544 static mlir::Value genIOUnitNumber(Fortran::lower::AbstractConverter &converter,
1545 mlir::Location loc,
1546 const Fortran::lower::SomeExpr *iounit,
1547 mlir::Type ty, ConditionSpecInfo &csi,
1548 Fortran::lower::StatementContext &stmtCtx) {
1549 auto &builder = converter.getFirOpBuilder();
1550 auto rawUnit = fir::getBase(converter.genExprValue(loc, iounit, stmtCtx));
1551 unsigned rawUnitWidth =
1552 rawUnit.getType().cast<mlir::IntegerType>().getWidth();
1553 unsigned runtimeArgWidth = ty.cast<mlir::IntegerType>().getWidth();
1554 // The IO runtime supports `int` unit numbers, if the unit number may
1555 // overflow when passed to the IO runtime, check that the unit number is
1556 // in range before calling the BeginXXX.
1557 if (rawUnitWidth > runtimeArgWidth) {
1558 mlir::func::FuncOp check =
1559 rawUnitWidth <= 64
1560 ? getIORuntimeFunc<mkIOKey(CheckUnitNumberInRange64)>(loc, builder)
1561 : getIORuntimeFunc<mkIOKey(CheckUnitNumberInRange128)>(loc,
1562 builder);
1563 mlir::FunctionType funcTy = check.getFunctionType();
1564 llvm::SmallVector<mlir::Value> args;
1565 args.push_back(builder.createConvert(loc, funcTy.getInput(0), rawUnit));
1566 args.push_back(builder.createBool(loc, csi.hasErrorConditionSpec()));
1567 if (csi.ioMsg) {
1568 args.push_back(builder.createConvert(loc, funcTy.getInput(2),
1569 fir::getBase(*csi.ioMsg)));
1570 args.push_back(builder.createConvert(loc, funcTy.getInput(3),
1571 fir::getLen(*csi.ioMsg)));
1572 } else {
1573 args.push_back(builder.createNullConstant(loc, funcTy.getInput(2)));
1574 args.push_back(
1575 fir::factory::createZeroValue(builder, loc, funcTy.getInput(3)));
1576 }
1577 mlir::Value file = locToFilename(converter, loc, funcTy.getInput(4));
1578 mlir::Value line = locToLineNo(converter, loc, funcTy.getInput(5));
1579 args.push_back(file);
1580 args.push_back(line);
1581 auto checkCall = builder.create<fir::CallOp>(loc, check, args);
1582 if (csi.hasErrorConditionSpec()) {
1583 mlir::Value iostat = checkCall.getResult(0);
1584 mlir::Type iostatTy = iostat.getType();
1585 mlir::Value zero = fir::factory::createZeroValue(builder, loc, iostatTy);
1586 mlir::Value unitIsOK = builder.create<mlir::arith::CmpIOp>(
1587 loc, mlir::arith::CmpIPredicate::eq, iostat, zero);
1588 auto ifOp = builder.create<fir::IfOp>(loc, iostatTy, unitIsOK,
1589 /*withElseRegion=*/true);
1590 builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
1591 builder.create<fir::ResultOp>(loc, iostat);
1592 builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
1593 stmtCtx.pushScope();
1594 csi.bigUnitIfOp = ifOp;
1595 }
1596 }
1597 return builder.createConvert(loc, ty, rawUnit);
1598 }
1599
genIOUnit(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::parser::IoUnit * iounit,mlir::Type ty,ConditionSpecInfo & csi,Fortran::lower::StatementContext & stmtCtx)1600 static mlir::Value genIOUnit(Fortran::lower::AbstractConverter &converter,
1601 mlir::Location loc,
1602 const Fortran::parser::IoUnit *iounit,
1603 mlir::Type ty, ConditionSpecInfo &csi,
1604 Fortran::lower::StatementContext &stmtCtx) {
1605 auto &builder = converter.getFirOpBuilder();
1606 if (iounit)
1607 if (auto *e = std::get_if<Fortran::parser::FileUnitNumber>(&iounit->u))
1608 return genIOUnitNumber(converter, loc, Fortran::semantics::GetExpr(*e),
1609 ty, csi, stmtCtx);
1610 return builder.create<mlir::arith::ConstantOp>(
1611 loc, builder.getIntegerAttr(ty, Fortran::runtime::io::DefaultUnit));
1612 }
1613
1614 template <typename A>
getIOUnit(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & stmt,mlir::Type ty,ConditionSpecInfo & csi,Fortran::lower::StatementContext & stmtCtx)1615 static mlir::Value getIOUnit(Fortran::lower::AbstractConverter &converter,
1616 mlir::Location loc, const A &stmt, mlir::Type ty,
1617 ConditionSpecInfo &csi,
1618 Fortran::lower::StatementContext &stmtCtx) {
1619 const Fortran::parser::IoUnit *iounit =
1620 stmt.iounit ? &*stmt.iounit : getIOControl<Fortran::parser::IoUnit>(stmt);
1621 return genIOUnit(converter, loc, iounit, ty, csi, stmtCtx);
1622 }
1623 //===----------------------------------------------------------------------===//
1624 // Generators for each IO statement type.
1625 //===----------------------------------------------------------------------===//
1626
1627 template <typename K, typename S>
genBasicIOStmt(Fortran::lower::AbstractConverter & converter,const S & stmt)1628 static mlir::Value genBasicIOStmt(Fortran::lower::AbstractConverter &converter,
1629 const S &stmt) {
1630 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1631 Fortran::lower::StatementContext stmtCtx;
1632 mlir::Location loc = converter.getCurrentLocation();
1633 ConditionSpecInfo csi = lowerErrorSpec(converter, loc, stmt.v);
1634 mlir::func::FuncOp beginFunc = getIORuntimeFunc<K>(loc, builder);
1635 mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
1636 mlir::Value unit = genIOUnitNumber(
1637 converter, loc, getExpr<Fortran::parser::FileUnitNumber>(stmt),
1638 beginFuncTy.getInput(0), csi, stmtCtx);
1639 mlir::Value un = builder.createConvert(loc, beginFuncTy.getInput(0), unit);
1640 mlir::Value file = locToFilename(converter, loc, beginFuncTy.getInput(1));
1641 mlir::Value line = locToLineNo(converter, loc, beginFuncTy.getInput(2));
1642 auto call = builder.create<fir::CallOp>(loc, beginFunc,
1643 mlir::ValueRange{un, file, line});
1644 mlir::Value cookie = call.getResult(0);
1645 genConditionHandlerCall(converter, loc, cookie, stmt.v, csi);
1646 mlir::Value ok;
1647 auto insertPt = builder.saveInsertionPoint();
1648 threadSpecs(converter, loc, cookie, stmt.v, csi.hasErrorConditionSpec(), ok);
1649 builder.restoreInsertionPoint(insertPt);
1650 return genEndIO(converter, converter.getCurrentLocation(), cookie, csi,
1651 stmtCtx);
1652 }
1653
genBackspaceStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::BackspaceStmt & stmt)1654 mlir::Value Fortran::lower::genBackspaceStatement(
1655 Fortran::lower::AbstractConverter &converter,
1656 const Fortran::parser::BackspaceStmt &stmt) {
1657 return genBasicIOStmt<mkIOKey(BeginBackspace)>(converter, stmt);
1658 }
1659
genEndfileStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::EndfileStmt & stmt)1660 mlir::Value Fortran::lower::genEndfileStatement(
1661 Fortran::lower::AbstractConverter &converter,
1662 const Fortran::parser::EndfileStmt &stmt) {
1663 return genBasicIOStmt<mkIOKey(BeginEndfile)>(converter, stmt);
1664 }
1665
1666 mlir::Value
genFlushStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::FlushStmt & stmt)1667 Fortran::lower::genFlushStatement(Fortran::lower::AbstractConverter &converter,
1668 const Fortran::parser::FlushStmt &stmt) {
1669 return genBasicIOStmt<mkIOKey(BeginFlush)>(converter, stmt);
1670 }
1671
1672 mlir::Value
genRewindStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::RewindStmt & stmt)1673 Fortran::lower::genRewindStatement(Fortran::lower::AbstractConverter &converter,
1674 const Fortran::parser::RewindStmt &stmt) {
1675 return genBasicIOStmt<mkIOKey(BeginRewind)>(converter, stmt);
1676 }
1677
1678 static mlir::Value
genNewunitSpec(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const std::list<Fortran::parser::ConnectSpec> & specList)1679 genNewunitSpec(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1680 mlir::Value cookie,
1681 const std::list<Fortran::parser::ConnectSpec> &specList) {
1682 for (const auto &spec : specList)
1683 if (auto *newunit =
1684 std::get_if<Fortran::parser::ConnectSpec::Newunit>(&spec.u)) {
1685 Fortran::lower::StatementContext stmtCtx;
1686 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1687 mlir::func::FuncOp ioFunc =
1688 getIORuntimeFunc<mkIOKey(GetNewUnit)>(loc, builder);
1689 mlir::FunctionType ioFuncTy = ioFunc.getFunctionType();
1690 const auto *var = Fortran::semantics::GetExpr(newunit->v);
1691 mlir::Value addr = builder.createConvert(
1692 loc, ioFuncTy.getInput(1),
1693 fir::getBase(converter.genExprAddr(loc, var, stmtCtx)));
1694 auto kind = builder.createIntegerConstant(loc, ioFuncTy.getInput(2),
1695 var->GetType().value().kind());
1696 llvm::SmallVector<mlir::Value> ioArgs = {cookie, addr, kind};
1697 return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
1698 }
1699 llvm_unreachable("missing Newunit spec");
1700 }
1701
1702 mlir::Value
genOpenStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::OpenStmt & stmt)1703 Fortran::lower::genOpenStatement(Fortran::lower::AbstractConverter &converter,
1704 const Fortran::parser::OpenStmt &stmt) {
1705 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1706 Fortran::lower::StatementContext stmtCtx;
1707 mlir::func::FuncOp beginFunc;
1708 llvm::SmallVector<mlir::Value> beginArgs;
1709 mlir::Location loc = converter.getCurrentLocation();
1710 ConditionSpecInfo csi = lowerErrorSpec(converter, loc, stmt.v);
1711 bool hasNewunitSpec = false;
1712 if (hasSpec<Fortran::parser::FileUnitNumber>(stmt)) {
1713 beginFunc = getIORuntimeFunc<mkIOKey(BeginOpenUnit)>(loc, builder);
1714 mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
1715 mlir::Value unit = genIOUnitNumber(
1716 converter, loc, getExpr<Fortran::parser::FileUnitNumber>(stmt),
1717 beginFuncTy.getInput(0), csi, stmtCtx);
1718 beginArgs.push_back(unit);
1719 beginArgs.push_back(locToFilename(converter, loc, beginFuncTy.getInput(1)));
1720 beginArgs.push_back(locToLineNo(converter, loc, beginFuncTy.getInput(2)));
1721 } else {
1722 hasNewunitSpec = hasSpec<Fortran::parser::ConnectSpec::Newunit>(stmt);
1723 assert(hasNewunitSpec && "missing unit specifier");
1724 beginFunc = getIORuntimeFunc<mkIOKey(BeginOpenNewUnit)>(loc, builder);
1725 mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
1726 beginArgs.push_back(locToFilename(converter, loc, beginFuncTy.getInput(0)));
1727 beginArgs.push_back(locToLineNo(converter, loc, beginFuncTy.getInput(1)));
1728 }
1729 auto cookie =
1730 builder.create<fir::CallOp>(loc, beginFunc, beginArgs).getResult(0);
1731 genConditionHandlerCall(converter, loc, cookie, stmt.v, csi);
1732 mlir::Value ok;
1733 auto insertPt = builder.saveInsertionPoint();
1734 threadSpecs(converter, loc, cookie, stmt.v, csi.hasErrorConditionSpec(), ok);
1735 if (hasNewunitSpec)
1736 genNewunitSpec(converter, loc, cookie, stmt.v);
1737 builder.restoreInsertionPoint(insertPt);
1738 return genEndIO(converter, loc, cookie, csi, stmtCtx);
1739 }
1740
1741 mlir::Value
genCloseStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::CloseStmt & stmt)1742 Fortran::lower::genCloseStatement(Fortran::lower::AbstractConverter &converter,
1743 const Fortran::parser::CloseStmt &stmt) {
1744 return genBasicIOStmt<mkIOKey(BeginClose)>(converter, stmt);
1745 }
1746
1747 mlir::Value
genWaitStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::WaitStmt & stmt)1748 Fortran::lower::genWaitStatement(Fortran::lower::AbstractConverter &converter,
1749 const Fortran::parser::WaitStmt &stmt) {
1750 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1751 Fortran::lower::StatementContext stmtCtx;
1752 mlir::Location loc = converter.getCurrentLocation();
1753 ConditionSpecInfo csi = lowerErrorSpec(converter, loc, stmt.v);
1754 bool hasId = hasSpec<Fortran::parser::IdExpr>(stmt);
1755 mlir::func::FuncOp beginFunc =
1756 hasId ? getIORuntimeFunc<mkIOKey(BeginWait)>(loc, builder)
1757 : getIORuntimeFunc<mkIOKey(BeginWaitAll)>(loc, builder);
1758 mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
1759 mlir::Value unit = genIOUnitNumber(
1760 converter, loc, getExpr<Fortran::parser::FileUnitNumber>(stmt),
1761 beginFuncTy.getInput(0), csi, stmtCtx);
1762 llvm::SmallVector<mlir::Value> args{unit};
1763 if (hasId) {
1764 mlir::Value id = fir::getBase(converter.genExprValue(
1765 loc, getExpr<Fortran::parser::IdExpr>(stmt), stmtCtx));
1766 args.push_back(builder.createConvert(loc, beginFuncTy.getInput(1), id));
1767 args.push_back(locToFilename(converter, loc, beginFuncTy.getInput(2)));
1768 args.push_back(locToLineNo(converter, loc, beginFuncTy.getInput(3)));
1769 } else {
1770 args.push_back(locToFilename(converter, loc, beginFuncTy.getInput(1)));
1771 args.push_back(locToLineNo(converter, loc, beginFuncTy.getInput(2)));
1772 }
1773 auto cookie = builder.create<fir::CallOp>(loc, beginFunc, args).getResult(0);
1774 genConditionHandlerCall(converter, loc, cookie, stmt.v, csi);
1775 return genEndIO(converter, converter.getCurrentLocation(), cookie, csi,
1776 stmtCtx);
1777 }
1778
1779 //===----------------------------------------------------------------------===//
1780 // Data transfer statements.
1781 //
1782 // There are several dimensions to the API with regard to data transfer
1783 // statements that need to be considered.
1784 //
1785 // - input (READ) vs. output (WRITE, PRINT)
1786 // - unformatted vs. formatted vs. list vs. namelist
1787 // - synchronous vs. asynchronous
1788 // - external vs. internal
1789 //===----------------------------------------------------------------------===//
1790
1791 // Get the begin data transfer IO function to call for the given values.
1792 template <bool isInput>
1793 mlir::func::FuncOp
getBeginDataTransferFunc(mlir::Location loc,fir::FirOpBuilder & builder,bool isFormatted,bool isListOrNml,bool isInternal,bool isInternalWithDesc,bool isAsync)1794 getBeginDataTransferFunc(mlir::Location loc, fir::FirOpBuilder &builder,
1795 bool isFormatted, bool isListOrNml, bool isInternal,
1796 bool isInternalWithDesc, bool isAsync) {
1797 if constexpr (isInput) {
1798 if (isFormatted || isListOrNml) {
1799 if (isInternal) {
1800 if (isInternalWithDesc) {
1801 if (isListOrNml)
1802 return getIORuntimeFunc<mkIOKey(BeginInternalArrayListInput)>(
1803 loc, builder);
1804 return getIORuntimeFunc<mkIOKey(BeginInternalArrayFormattedInput)>(
1805 loc, builder);
1806 }
1807 if (isListOrNml)
1808 return getIORuntimeFunc<mkIOKey(BeginInternalListInput)>(loc,
1809 builder);
1810 return getIORuntimeFunc<mkIOKey(BeginInternalFormattedInput)>(loc,
1811 builder);
1812 }
1813 if (isListOrNml)
1814 return getIORuntimeFunc<mkIOKey(BeginExternalListInput)>(loc, builder);
1815 return getIORuntimeFunc<mkIOKey(BeginExternalFormattedInput)>(loc,
1816 builder);
1817 }
1818 return getIORuntimeFunc<mkIOKey(BeginUnformattedInput)>(loc, builder);
1819 } else {
1820 if (isFormatted || isListOrNml) {
1821 if (isInternal) {
1822 if (isInternalWithDesc) {
1823 if (isListOrNml)
1824 return getIORuntimeFunc<mkIOKey(BeginInternalArrayListOutput)>(
1825 loc, builder);
1826 return getIORuntimeFunc<mkIOKey(BeginInternalArrayFormattedOutput)>(
1827 loc, builder);
1828 }
1829 if (isListOrNml)
1830 return getIORuntimeFunc<mkIOKey(BeginInternalListOutput)>(loc,
1831 builder);
1832 return getIORuntimeFunc<mkIOKey(BeginInternalFormattedOutput)>(loc,
1833 builder);
1834 }
1835 if (isListOrNml)
1836 return getIORuntimeFunc<mkIOKey(BeginExternalListOutput)>(loc, builder);
1837 return getIORuntimeFunc<mkIOKey(BeginExternalFormattedOutput)>(loc,
1838 builder);
1839 }
1840 return getIORuntimeFunc<mkIOKey(BeginUnformattedOutput)>(loc, builder);
1841 }
1842 }
1843
1844 /// Generate the arguments of a begin data transfer statement call.
1845 template <bool hasIOCtrl, typename A>
genBeginDataTransferCallArgs(llvm::SmallVectorImpl<mlir::Value> & ioArgs,Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & stmt,mlir::FunctionType ioFuncTy,bool isFormatted,bool isListOrNml,bool isInternal,bool isAsync,const llvm::Optional<fir::ExtendedValue> & descRef,ConditionSpecInfo & csi,Fortran::lower::StatementContext & stmtCtx)1846 void genBeginDataTransferCallArgs(
1847 llvm::SmallVectorImpl<mlir::Value> &ioArgs,
1848 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1849 const A &stmt, mlir::FunctionType ioFuncTy, bool isFormatted,
1850 bool isListOrNml, [[maybe_unused]] bool isInternal,
1851 [[maybe_unused]] bool isAsync,
1852 const llvm::Optional<fir::ExtendedValue> &descRef, ConditionSpecInfo &csi,
1853 Fortran::lower::StatementContext &stmtCtx) {
1854 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1855 auto maybeGetFormatArgs = [&]() {
1856 if (!isFormatted || isListOrNml)
1857 return;
1858 auto pair =
1859 getFormat(converter, loc, stmt, ioFuncTy.getInput(ioArgs.size()),
1860 ioFuncTy.getInput(ioArgs.size() + 1), stmtCtx);
1861 ioArgs.push_back(std::get<0>(pair)); // format character string
1862 ioArgs.push_back(std::get<1>(pair)); // format length
1863 };
1864 if constexpr (hasIOCtrl) { // READ or WRITE
1865 if (isInternal) {
1866 // descriptor or scalar variable; maybe explicit format; scratch area
1867 if (descRef) {
1868 mlir::Value desc = builder.createBox(loc, *descRef);
1869 ioArgs.push_back(
1870 builder.createConvert(loc, ioFuncTy.getInput(ioArgs.size()), desc));
1871 } else {
1872 std::tuple<mlir::Value, mlir::Value> pair =
1873 getBuffer(converter, loc, stmt, ioFuncTy.getInput(ioArgs.size()),
1874 ioFuncTy.getInput(ioArgs.size() + 1), stmtCtx);
1875 ioArgs.push_back(std::get<0>(pair)); // scalar character variable
1876 ioArgs.push_back(std::get<1>(pair)); // character length
1877 }
1878 maybeGetFormatArgs();
1879 ioArgs.push_back( // internal scratch area buffer
1880 getDefaultScratch(builder, loc, ioFuncTy.getInput(ioArgs.size())));
1881 ioArgs.push_back( // buffer length
1882 getDefaultScratchLen(builder, loc, ioFuncTy.getInput(ioArgs.size())));
1883 } else { // external IO - maybe explicit format; unit
1884 if (isAsync)
1885 TODO(loc, "asynchronous");
1886 maybeGetFormatArgs();
1887 ioArgs.push_back(getIOUnit(converter, loc, stmt,
1888 ioFuncTy.getInput(ioArgs.size()), csi,
1889 stmtCtx));
1890 }
1891 } else { // PRINT - maybe explicit format; default unit
1892 maybeGetFormatArgs();
1893 ioArgs.push_back(builder.create<mlir::arith::ConstantOp>(
1894 loc, builder.getIntegerAttr(ioFuncTy.getInput(ioArgs.size()),
1895 Fortran::runtime::io::DefaultUnit)));
1896 }
1897 // File name and line number are always the last two arguments.
1898 ioArgs.push_back(
1899 locToFilename(converter, loc, ioFuncTy.getInput(ioArgs.size())));
1900 ioArgs.push_back(
1901 locToLineNo(converter, loc, ioFuncTy.getInput(ioArgs.size())));
1902 }
1903
1904 template <bool isInput, bool hasIOCtrl = true, typename A>
1905 static mlir::Value
genDataTransferStmt(Fortran::lower::AbstractConverter & converter,const A & stmt)1906 genDataTransferStmt(Fortran::lower::AbstractConverter &converter,
1907 const A &stmt) {
1908 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1909 Fortran::lower::StatementContext stmtCtx;
1910 mlir::Location loc = converter.getCurrentLocation();
1911 const bool isFormatted = isDataTransferFormatted(stmt);
1912 const bool isList = isFormatted ? isDataTransferList(stmt) : false;
1913 const bool isInternal = isDataTransferInternal(stmt);
1914 llvm::Optional<fir::ExtendedValue> descRef =
1915 isInternal ? maybeGetInternalIODescriptor(converter, stmt, stmtCtx)
1916 : llvm::None;
1917 const bool isInternalWithDesc = descRef.has_value();
1918 const bool isAsync = isDataTransferAsynchronous(loc, stmt);
1919 const bool isNml = isDataTransferNamelist(stmt);
1920
1921 // Generate an EnableHandlers call and remaining specifier calls.
1922 ConditionSpecInfo csi;
1923 if constexpr (hasIOCtrl) {
1924 csi = lowerErrorSpec(converter, loc, stmt.controls);
1925 }
1926
1927 // Generate the begin data transfer function call.
1928 mlir::func::FuncOp ioFunc = getBeginDataTransferFunc<isInput>(
1929 loc, builder, isFormatted, isList || isNml, isInternal,
1930 isInternalWithDesc, isAsync);
1931 llvm::SmallVector<mlir::Value> ioArgs;
1932 genBeginDataTransferCallArgs<hasIOCtrl>(
1933 ioArgs, converter, loc, stmt, ioFunc.getFunctionType(), isFormatted,
1934 isList || isNml, isInternal, isAsync, descRef, csi, stmtCtx);
1935 mlir::Value cookie =
1936 builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
1937
1938 auto insertPt = builder.saveInsertionPoint();
1939 mlir::Value ok;
1940 if constexpr (hasIOCtrl) {
1941 genConditionHandlerCall(converter, loc, cookie, stmt.controls, csi);
1942 threadSpecs(converter, loc, cookie, stmt.controls,
1943 csi.hasErrorConditionSpec(), ok);
1944 }
1945
1946 // Generate data transfer list calls.
1947 if constexpr (isInput) { // READ
1948 if (isNml)
1949 genNamelistIO(converter, cookie,
1950 getIORuntimeFunc<mkIOKey(InputNamelist)>(loc, builder),
1951 *getIOControl<Fortran::parser::Name>(stmt)->symbol,
1952 csi.hasTransferConditionSpec(), ok, stmtCtx);
1953 else
1954 genInputItemList(converter, cookie, stmt.items, isFormatted,
1955 csi.hasTransferConditionSpec(), ok, /*inLoop=*/false);
1956 } else if constexpr (std::is_same_v<A, Fortran::parser::WriteStmt>) {
1957 if (isNml)
1958 genNamelistIO(converter, cookie,
1959 getIORuntimeFunc<mkIOKey(OutputNamelist)>(loc, builder),
1960 *getIOControl<Fortran::parser::Name>(stmt)->symbol,
1961 csi.hasTransferConditionSpec(), ok, stmtCtx);
1962 else
1963 genOutputItemList(converter, cookie, stmt.items, isFormatted,
1964 csi.hasTransferConditionSpec(), ok,
1965 /*inLoop=*/false);
1966 } else { // PRINT
1967 genOutputItemList(converter, cookie, std::get<1>(stmt.t), isFormatted,
1968 csi.hasTransferConditionSpec(), ok,
1969 /*inLoop=*/false);
1970 }
1971 stmtCtx.finalize();
1972
1973 builder.restoreInsertionPoint(insertPt);
1974 if constexpr (hasIOCtrl) {
1975 genIOReadSize(converter, loc, cookie, stmt.controls,
1976 csi.hasErrorConditionSpec());
1977 }
1978 // Generate end statement call/s.
1979 return genEndIO(converter, loc, cookie, csi, stmtCtx);
1980 }
1981
genPrintStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::PrintStmt & stmt)1982 void Fortran::lower::genPrintStatement(
1983 Fortran::lower::AbstractConverter &converter,
1984 const Fortran::parser::PrintStmt &stmt) {
1985 // PRINT does not take an io-control-spec. It only has a format specifier, so
1986 // it is a simplified case of WRITE.
1987 genDataTransferStmt</*isInput=*/false, /*ioCtrl=*/false>(converter, stmt);
1988 }
1989
1990 mlir::Value
genWriteStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::WriteStmt & stmt)1991 Fortran::lower::genWriteStatement(Fortran::lower::AbstractConverter &converter,
1992 const Fortran::parser::WriteStmt &stmt) {
1993 return genDataTransferStmt</*isInput=*/false>(converter, stmt);
1994 }
1995
1996 mlir::Value
genReadStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::ReadStmt & stmt)1997 Fortran::lower::genReadStatement(Fortran::lower::AbstractConverter &converter,
1998 const Fortran::parser::ReadStmt &stmt) {
1999 return genDataTransferStmt</*isInput=*/true>(converter, stmt);
2000 }
2001
2002 /// Get the file expression from the inquire spec list. Also return if the
2003 /// expression is a file name.
2004 static std::pair<const Fortran::lower::SomeExpr *, bool>
getInquireFileExpr(const std::list<Fortran::parser::InquireSpec> * stmt)2005 getInquireFileExpr(const std::list<Fortran::parser::InquireSpec> *stmt) {
2006 if (!stmt)
2007 return {nullptr, /*filename?=*/false};
2008 for (const Fortran::parser::InquireSpec &spec : *stmt) {
2009 if (auto *f = std::get_if<Fortran::parser::FileUnitNumber>(&spec.u))
2010 return {Fortran::semantics::GetExpr(*f), /*filename?=*/false};
2011 if (auto *f = std::get_if<Fortran::parser::FileNameExpr>(&spec.u))
2012 return {Fortran::semantics::GetExpr(*f), /*filename?=*/true};
2013 }
2014 // semantics should have already caught this condition
2015 llvm::report_fatal_error("inquire spec must have a file");
2016 }
2017
2018 /// Generate calls to the four distinct INQUIRE subhandlers. An INQUIRE may
2019 /// return values of type CHARACTER, INTEGER, or LOGICAL. There is one
2020 /// additional special case for INQUIRE with both PENDING and ID specifiers.
2021 template <typename A>
genInquireSpec(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,mlir::Value idExpr,const A & var,Fortran::lower::StatementContext & stmtCtx)2022 static mlir::Value genInquireSpec(Fortran::lower::AbstractConverter &converter,
2023 mlir::Location loc, mlir::Value cookie,
2024 mlir::Value idExpr, const A &var,
2025 Fortran::lower::StatementContext &stmtCtx) {
2026 // default case: do nothing
2027 return {};
2028 }
2029 /// Specialization for CHARACTER.
2030 template <>
genInquireSpec(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,mlir::Value idExpr,const Fortran::parser::InquireSpec::CharVar & var,Fortran::lower::StatementContext & stmtCtx)2031 mlir::Value genInquireSpec<Fortran::parser::InquireSpec::CharVar>(
2032 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
2033 mlir::Value cookie, mlir::Value idExpr,
2034 const Fortran::parser::InquireSpec::CharVar &var,
2035 Fortran::lower::StatementContext &stmtCtx) {
2036 // IOMSG is handled with exception conditions
2037 if (std::get<Fortran::parser::InquireSpec::CharVar::Kind>(var.t) ==
2038 Fortran::parser::InquireSpec::CharVar::Kind::Iomsg)
2039 return {};
2040 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2041 mlir::func::FuncOp specFunc =
2042 getIORuntimeFunc<mkIOKey(InquireCharacter)>(loc, builder);
2043 mlir::FunctionType specFuncTy = specFunc.getFunctionType();
2044 const auto *varExpr = Fortran::semantics::GetExpr(
2045 std::get<Fortran::parser::ScalarDefaultCharVariable>(var.t));
2046 fir::ExtendedValue str = converter.genExprAddr(loc, varExpr, stmtCtx);
2047 llvm::SmallVector<mlir::Value> args = {
2048 builder.createConvert(loc, specFuncTy.getInput(0), cookie),
2049 builder.createIntegerConstant(
2050 loc, specFuncTy.getInput(1),
2051 Fortran::runtime::io::HashInquiryKeyword(
2052 Fortran::parser::InquireSpec::CharVar::EnumToString(
2053 std::get<Fortran::parser::InquireSpec::CharVar::Kind>(var.t))
2054 .c_str())),
2055 builder.createConvert(loc, specFuncTy.getInput(2), fir::getBase(str)),
2056 builder.createConvert(loc, specFuncTy.getInput(3), fir::getLen(str))};
2057 return builder.create<fir::CallOp>(loc, specFunc, args).getResult(0);
2058 }
2059 /// Specialization for INTEGER.
2060 template <>
genInquireSpec(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,mlir::Value idExpr,const Fortran::parser::InquireSpec::IntVar & var,Fortran::lower::StatementContext & stmtCtx)2061 mlir::Value genInquireSpec<Fortran::parser::InquireSpec::IntVar>(
2062 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
2063 mlir::Value cookie, mlir::Value idExpr,
2064 const Fortran::parser::InquireSpec::IntVar &var,
2065 Fortran::lower::StatementContext &stmtCtx) {
2066 // IOSTAT is handled with exception conditions
2067 if (std::get<Fortran::parser::InquireSpec::IntVar::Kind>(var.t) ==
2068 Fortran::parser::InquireSpec::IntVar::Kind::Iostat)
2069 return {};
2070 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2071 mlir::func::FuncOp specFunc =
2072 getIORuntimeFunc<mkIOKey(InquireInteger64)>(loc, builder);
2073 mlir::FunctionType specFuncTy = specFunc.getFunctionType();
2074 const auto *varExpr = Fortran::semantics::GetExpr(
2075 std::get<Fortran::parser::ScalarIntVariable>(var.t));
2076 mlir::Value addr = fir::getBase(converter.genExprAddr(loc, varExpr, stmtCtx));
2077 mlir::Type eleTy = fir::dyn_cast_ptrEleTy(addr.getType());
2078 if (!eleTy)
2079 fir::emitFatalError(loc,
2080 "internal error: expected a memory reference type");
2081 auto width = eleTy.cast<mlir::IntegerType>().getWidth();
2082 mlir::IndexType idxTy = builder.getIndexType();
2083 mlir::Value kind = builder.createIntegerConstant(loc, idxTy, width / 8);
2084 llvm::SmallVector<mlir::Value> args = {
2085 builder.createConvert(loc, specFuncTy.getInput(0), cookie),
2086 builder.createIntegerConstant(
2087 loc, specFuncTy.getInput(1),
2088 Fortran::runtime::io::HashInquiryKeyword(
2089 Fortran::parser::InquireSpec::IntVar::EnumToString(
2090 std::get<Fortran::parser::InquireSpec::IntVar::Kind>(var.t))
2091 .c_str())),
2092 builder.createConvert(loc, specFuncTy.getInput(2), addr),
2093 builder.createConvert(loc, specFuncTy.getInput(3), kind)};
2094 return builder.create<fir::CallOp>(loc, specFunc, args).getResult(0);
2095 }
2096 /// Specialization for LOGICAL and (PENDING + ID).
2097 template <>
genInquireSpec(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,mlir::Value idExpr,const Fortran::parser::InquireSpec::LogVar & var,Fortran::lower::StatementContext & stmtCtx)2098 mlir::Value genInquireSpec<Fortran::parser::InquireSpec::LogVar>(
2099 Fortran::lower::AbstractConverter &converter, mlir::Location loc,
2100 mlir::Value cookie, mlir::Value idExpr,
2101 const Fortran::parser::InquireSpec::LogVar &var,
2102 Fortran::lower::StatementContext &stmtCtx) {
2103 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2104 auto logVarKind = std::get<Fortran::parser::InquireSpec::LogVar::Kind>(var.t);
2105 bool pendId =
2106 idExpr &&
2107 logVarKind == Fortran::parser::InquireSpec::LogVar::Kind::Pending;
2108 mlir::func::FuncOp specFunc =
2109 pendId ? getIORuntimeFunc<mkIOKey(InquirePendingId)>(loc, builder)
2110 : getIORuntimeFunc<mkIOKey(InquireLogical)>(loc, builder);
2111 mlir::FunctionType specFuncTy = specFunc.getFunctionType();
2112 mlir::Value addr = fir::getBase(converter.genExprAddr(
2113 loc,
2114 Fortran::semantics::GetExpr(
2115 std::get<Fortran::parser::Scalar<
2116 Fortran::parser::Logical<Fortran::parser::Variable>>>(var.t)),
2117 stmtCtx));
2118 llvm::SmallVector<mlir::Value> args = {
2119 builder.createConvert(loc, specFuncTy.getInput(0), cookie)};
2120 if (pendId)
2121 args.push_back(builder.createConvert(loc, specFuncTy.getInput(1), idExpr));
2122 else
2123 args.push_back(builder.createIntegerConstant(
2124 loc, specFuncTy.getInput(1),
2125 Fortran::runtime::io::HashInquiryKeyword(
2126 Fortran::parser::InquireSpec::LogVar::EnumToString(logVarKind)
2127 .c_str())));
2128 args.push_back(builder.createConvert(loc, specFuncTy.getInput(2), addr));
2129 auto call = builder.create<fir::CallOp>(loc, specFunc, args);
2130 boolRefToLogical(loc, builder, addr);
2131 return call.getResult(0);
2132 }
2133
2134 /// If there is an IdExpr in the list of inquire-specs, then lower it and return
2135 /// the resulting Value. Otherwise, return null.
2136 static mlir::Value
lowerIdExpr(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const std::list<Fortran::parser::InquireSpec> & ispecs,Fortran::lower::StatementContext & stmtCtx)2137 lowerIdExpr(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
2138 const std::list<Fortran::parser::InquireSpec> &ispecs,
2139 Fortran::lower::StatementContext &stmtCtx) {
2140 for (const Fortran::parser::InquireSpec &spec : ispecs)
2141 if (mlir::Value v = std::visit(
2142 Fortran::common::visitors{
2143 [&](const Fortran::parser::IdExpr &idExpr) {
2144 return fir::getBase(converter.genExprValue(
2145 loc, Fortran::semantics::GetExpr(idExpr), stmtCtx));
2146 },
2147 [](const auto &) { return mlir::Value{}; }},
2148 spec.u))
2149 return v;
2150 return {};
2151 }
2152
2153 /// For each inquire-spec, build the appropriate call, threading the cookie.
threadInquire(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const std::list<Fortran::parser::InquireSpec> & ispecs,bool checkResult,mlir::Value & ok,Fortran::lower::StatementContext & stmtCtx)2154 static void threadInquire(Fortran::lower::AbstractConverter &converter,
2155 mlir::Location loc, mlir::Value cookie,
2156 const std::list<Fortran::parser::InquireSpec> &ispecs,
2157 bool checkResult, mlir::Value &ok,
2158 Fortran::lower::StatementContext &stmtCtx) {
2159 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2160 mlir::Value idExpr = lowerIdExpr(converter, loc, ispecs, stmtCtx);
2161 for (const Fortran::parser::InquireSpec &spec : ispecs) {
2162 makeNextConditionalOn(builder, loc, checkResult, ok);
2163 ok = std::visit(Fortran::common::visitors{[&](const auto &x) {
2164 return genInquireSpec(converter, loc, cookie, idExpr, x,
2165 stmtCtx);
2166 }},
2167 spec.u);
2168 }
2169 }
2170
genInquireStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::InquireStmt & stmt)2171 mlir::Value Fortran::lower::genInquireStatement(
2172 Fortran::lower::AbstractConverter &converter,
2173 const Fortran::parser::InquireStmt &stmt) {
2174 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2175 Fortran::lower::StatementContext stmtCtx;
2176 mlir::Location loc = converter.getCurrentLocation();
2177 mlir::func::FuncOp beginFunc;
2178 llvm::SmallVector<mlir::Value> beginArgs;
2179 const auto *list =
2180 std::get_if<std::list<Fortran::parser::InquireSpec>>(&stmt.u);
2181 auto exprPair = getInquireFileExpr(list);
2182 auto inquireFileUnit = [&]() -> bool {
2183 return exprPair.first && !exprPair.second;
2184 };
2185 auto inquireFileName = [&]() -> bool {
2186 return exprPair.first && exprPair.second;
2187 };
2188
2189 ConditionSpecInfo csi =
2190 list ? lowerErrorSpec(converter, loc, *list) : ConditionSpecInfo{};
2191
2192 // Make one of three BeginInquire calls.
2193 if (inquireFileUnit()) {
2194 // Inquire by unit -- [UNIT=]file-unit-number.
2195 beginFunc = getIORuntimeFunc<mkIOKey(BeginInquireUnit)>(loc, builder);
2196 mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
2197 mlir::Value unit = genIOUnitNumber(converter, loc, exprPair.first,
2198 beginFuncTy.getInput(0), csi, stmtCtx);
2199 beginArgs = {unit, locToFilename(converter, loc, beginFuncTy.getInput(1)),
2200 locToLineNo(converter, loc, beginFuncTy.getInput(2))};
2201 } else if (inquireFileName()) {
2202 // Inquire by file -- FILE=file-name-expr.
2203 beginFunc = getIORuntimeFunc<mkIOKey(BeginInquireFile)>(loc, builder);
2204 mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
2205 fir::ExtendedValue file =
2206 converter.genExprAddr(loc, exprPair.first, stmtCtx);
2207 beginArgs = {
2208 builder.createConvert(loc, beginFuncTy.getInput(0), fir::getBase(file)),
2209 builder.createConvert(loc, beginFuncTy.getInput(1), fir::getLen(file)),
2210 locToFilename(converter, loc, beginFuncTy.getInput(2)),
2211 locToLineNo(converter, loc, beginFuncTy.getInput(3))};
2212 } else {
2213 // Inquire by output list -- IOLENGTH=scalar-int-variable.
2214 const auto *ioLength =
2215 std::get_if<Fortran::parser::InquireStmt::Iolength>(&stmt.u);
2216 assert(ioLength && "must have an IOLENGTH specifier");
2217 beginFunc = getIORuntimeFunc<mkIOKey(BeginInquireIoLength)>(loc, builder);
2218 mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
2219 beginArgs = {locToFilename(converter, loc, beginFuncTy.getInput(0)),
2220 locToLineNo(converter, loc, beginFuncTy.getInput(1))};
2221 auto cookie =
2222 builder.create<fir::CallOp>(loc, beginFunc, beginArgs).getResult(0);
2223 mlir::Value ok;
2224 genOutputItemList(
2225 converter, cookie,
2226 std::get<std::list<Fortran::parser::OutputItem>>(ioLength->t),
2227 /*isFormatted=*/false, /*checkResult=*/false, ok, /*inLoop=*/false);
2228 auto *ioLengthVar = Fortran::semantics::GetExpr(
2229 std::get<Fortran::parser::ScalarIntVariable>(ioLength->t));
2230 mlir::Value ioLengthVarAddr =
2231 fir::getBase(converter.genExprAddr(loc, ioLengthVar, stmtCtx));
2232 llvm::SmallVector<mlir::Value> args = {cookie};
2233 mlir::Value length =
2234 builder
2235 .create<fir::CallOp>(
2236 loc, getIORuntimeFunc<mkIOKey(GetIoLength)>(loc, builder), args)
2237 .getResult(0);
2238 mlir::Value length1 =
2239 builder.createConvert(loc, converter.genType(*ioLengthVar), length);
2240 builder.create<fir::StoreOp>(loc, length1, ioLengthVarAddr);
2241 return genEndIO(converter, loc, cookie, csi, stmtCtx);
2242 }
2243
2244 // Common handling for inquire by unit or file.
2245 assert(list && "inquire-spec list must be present");
2246 auto cookie =
2247 builder.create<fir::CallOp>(loc, beginFunc, beginArgs).getResult(0);
2248 genConditionHandlerCall(converter, loc, cookie, *list, csi);
2249 // Handle remaining arguments in specifier list.
2250 mlir::Value ok;
2251 auto insertPt = builder.saveInsertionPoint();
2252 threadInquire(converter, loc, cookie, *list, csi.hasErrorConditionSpec(), ok,
2253 stmtCtx);
2254 builder.restoreInsertionPoint(insertPt);
2255 // Generate end statement call.
2256 return genEndIO(converter, loc, cookie, csi, stmtCtx);
2257 }
2258