• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- IO.cpp -- I/O 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 #include "flang/Lower/IO.h"
10 #include "../../runtime/io-api.h"
11 #include "RTBuilder.h"
12 #include "flang/Lower/Bridge.h"
13 #include "flang/Lower/CharacterExpr.h"
14 #include "flang/Lower/ComplexExpr.h"
15 #include "flang/Lower/FIRBuilder.h"
16 #include "flang/Lower/PFTBuilder.h"
17 #include "flang/Lower/Runtime.h"
18 #include "flang/Lower/Utils.h"
19 #include "flang/Parser/parse-tree.h"
20 #include "flang/Semantics/tools.h"
21 #include "mlir/Dialect/StandardOps/IR/Ops.h"
22 
23 #define TODO() llvm_unreachable("not yet implemented")
24 
25 using namespace Fortran::runtime::io;
26 
27 #define NAMIFY_HELPER(X) #X
28 #define NAMIFY(X) NAMIFY_HELPER(IONAME(X))
29 #define mkIOKey(X) mkKey(IONAME(X))
30 
31 namespace Fortran::lower {
32 /// Static table of IO runtime calls
33 ///
34 /// This logical map contains the name and type builder function for each IO
35 /// runtime function listed in the tuple. This table is fully constructed at
36 /// compile-time. Use the `mkIOKey` macro to access the table.
37 static constexpr std::tuple<
38     mkIOKey(BeginInternalArrayListOutput), mkIOKey(BeginInternalArrayListInput),
39     mkIOKey(BeginInternalArrayFormattedOutput),
40     mkIOKey(BeginInternalArrayFormattedInput), mkIOKey(BeginInternalListOutput),
41     mkIOKey(BeginInternalListInput), mkIOKey(BeginInternalFormattedOutput),
42     mkIOKey(BeginInternalFormattedInput), mkIOKey(BeginInternalNamelistOutput),
43     mkIOKey(BeginInternalNamelistInput), mkIOKey(BeginExternalListOutput),
44     mkIOKey(BeginExternalListInput), mkIOKey(BeginExternalFormattedOutput),
45     mkIOKey(BeginExternalFormattedInput), mkIOKey(BeginUnformattedOutput),
46     mkIOKey(BeginUnformattedInput), mkIOKey(BeginExternalNamelistOutput),
47     mkIOKey(BeginExternalNamelistInput), mkIOKey(BeginAsynchronousOutput),
48     mkIOKey(BeginAsynchronousInput), mkIOKey(BeginWait), mkIOKey(BeginWaitAll),
49     mkIOKey(BeginClose), mkIOKey(BeginFlush), mkIOKey(BeginBackspace),
50     mkIOKey(BeginEndfile), mkIOKey(BeginRewind), mkIOKey(BeginOpenUnit),
51     mkIOKey(BeginOpenNewUnit), mkIOKey(BeginInquireUnit),
52     mkIOKey(BeginInquireFile), mkIOKey(BeginInquireIoLength),
53     mkIOKey(EnableHandlers), mkIOKey(SetAdvance), mkIOKey(SetBlank),
54     mkIOKey(SetDecimal), mkIOKey(SetDelim), mkIOKey(SetPad), mkIOKey(SetPos),
55     mkIOKey(SetRec), mkIOKey(SetRound), mkIOKey(SetSign),
56     mkIOKey(OutputDescriptor), mkIOKey(InputDescriptor),
57     mkIOKey(OutputUnformattedBlock), mkIOKey(InputUnformattedBlock),
58     mkIOKey(OutputInteger64), mkIOKey(InputInteger), mkIOKey(OutputReal32),
59     mkIOKey(InputReal32), mkIOKey(OutputReal64), mkIOKey(InputReal64),
60     mkIOKey(OutputComplex64), mkIOKey(OutputComplex32), mkIOKey(OutputAscii),
61     mkIOKey(InputAscii), mkIOKey(OutputLogical), mkIOKey(InputLogical),
62     mkIOKey(SetAccess), mkIOKey(SetAction), mkIOKey(SetAsynchronous),
63     mkIOKey(SetCarriagecontrol), mkIOKey(SetEncoding), mkIOKey(SetForm),
64     mkIOKey(SetPosition), mkIOKey(SetRecl), mkIOKey(SetStatus),
65     mkIOKey(SetFile), mkIOKey(GetNewUnit), mkIOKey(GetSize),
66     mkIOKey(GetIoLength), mkIOKey(GetIoMsg), mkIOKey(InquireCharacter),
67     mkIOKey(InquireLogical), mkIOKey(InquirePendingId),
68     mkIOKey(InquireInteger64), mkIOKey(EndIoStatement)>
69     newIOTable;
70 } // namespace Fortran::lower
71 
72 namespace {
73 struct ConditionSpecifierInfo {
74   const Fortran::semantics::SomeExpr *ioStatExpr{};
75   const Fortran::semantics::SomeExpr *ioMsgExpr{};
76   bool hasErr{};
77   bool hasEnd{};
78   bool hasEor{};
79 
80   /// Check for any condition specifier that applies to specifier processing.
hasErrorConditionSpecifier__anon61302d640111::ConditionSpecifierInfo81   bool hasErrorConditionSpecifier() const {
82     return ioStatExpr != nullptr || hasErr;
83   }
84   /// Check for any condition specifier that applies to data transfer items
85   /// in a PRINT, READ, WRITE, or WAIT statement.  (WAIT may be irrelevant.)
hasTransferConditionSpecifier__anon61302d640111::ConditionSpecifierInfo86   bool hasTransferConditionSpecifier() const {
87     return ioStatExpr != nullptr || hasErr || hasEnd || hasEor;
88   }
89   /// Check for any condition specifier, including IOMSG.
hasAnyConditionSpecifier__anon61302d640111::ConditionSpecifierInfo90   bool hasAnyConditionSpecifier() const {
91     return ioStatExpr != nullptr || ioMsgExpr != nullptr || hasErr || hasEnd ||
92            hasEor;
93   }
94 };
95 } // namespace
96 
97 using namespace Fortran::lower;
98 
99 /// Helper function to retrieve the name of the IO function given the key `A`
100 template <typename A>
getName()101 static constexpr const char *getName() {
102   return std::get<A>(newIOTable).name;
103 }
104 
105 /// Helper function to retrieve the type model signature builder of the IO
106 /// function as defined by the key `A`
107 template <typename A>
getTypeModel()108 static constexpr FuncTypeBuilderFunc getTypeModel() {
109   return std::get<A>(newIOTable).getTypeModel();
110 }
111 
getLength(mlir::Type argTy)112 inline int64_t getLength(mlir::Type argTy) {
113   return argTy.cast<fir::SequenceType>().getShape()[0];
114 }
115 
116 /// Get (or generate) the MLIR FuncOp for a given IO runtime function.
117 template <typename E>
getIORuntimeFunc(mlir::Location loc,Fortran::lower::FirOpBuilder & builder)118 static mlir::FuncOp getIORuntimeFunc(mlir::Location loc,
119                                      Fortran::lower::FirOpBuilder &builder) {
120   auto name = getName<E>();
121   auto func = builder.getNamedFunction(name);
122   if (func)
123     return func;
124   auto funTy = getTypeModel<E>()(builder.getContext());
125   func = builder.createFunction(loc, name, funTy);
126   func.setAttr("fir.runtime", builder.getUnitAttr());
127   func.setAttr("fir.io", builder.getUnitAttr());
128   return func;
129 }
130 
131 /// Generate calls to end an IO statement.  Return the IOSTAT value, if any.
132 /// It is the caller's responsibility to generate branches on that value.
genEndIO(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const ConditionSpecifierInfo & csi)133 static mlir::Value genEndIO(Fortran::lower::AbstractConverter &converter,
134                             mlir::Location loc, mlir::Value cookie,
135                             const ConditionSpecifierInfo &csi) {
136   auto &builder = converter.getFirOpBuilder();
137   if (csi.ioMsgExpr) {
138     auto getIoMsg = getIORuntimeFunc<mkIOKey(GetIoMsg)>(loc, builder);
139     auto ioMsgVar =
140         Fortran::lower::CharacterExprHelper{builder, loc}.createUnboxChar(
141             converter.genExprAddr(csi.ioMsgExpr, loc));
142     llvm::SmallVector<mlir::Value, 3> args{
143         cookie,
144         builder.createConvert(loc, getIoMsg.getType().getInput(1),
145                               ioMsgVar.first),
146         builder.createConvert(loc, getIoMsg.getType().getInput(2),
147                               ioMsgVar.second)};
148     builder.create<mlir::CallOp>(loc, getIoMsg, args);
149   }
150   auto endIoStatement = getIORuntimeFunc<mkIOKey(EndIoStatement)>(loc, builder);
151   llvm::SmallVector<mlir::Value, 1> endArgs{cookie};
152   auto call = builder.create<mlir::CallOp>(loc, endIoStatement, endArgs);
153   if (csi.ioStatExpr) {
154     auto ioStatVar = converter.genExprAddr(csi.ioStatExpr, loc);
155     auto ioStatResult = builder.createConvert(
156         loc, converter.genType(*csi.ioStatExpr), call.getResult(0));
157     builder.create<fir::StoreOp>(loc, ioStatResult, ioStatVar);
158   }
159   return csi.hasTransferConditionSpecifier() ? call.getResult(0)
160                                              : mlir::Value{};
161 }
162 
163 /// Make the next call in the IO statement conditional on runtime result `ok`.
164 /// If a call returns `ok==false`, further suboperation calls for an I/O
165 /// statement will be skipped.  This may generate branch heavy, deeply nested
166 /// conditionals for I/O statements with a large number of suboperations.
makeNextConditionalOn(Fortran::lower::FirOpBuilder & builder,mlir::Location loc,mlir::OpBuilder::InsertPoint & insertPt,bool checkResult,mlir::Value ok,bool inIterWhileLoop=false)167 static void makeNextConditionalOn(Fortran::lower::FirOpBuilder &builder,
168                                   mlir::Location loc,
169                                   mlir::OpBuilder::InsertPoint &insertPt,
170                                   bool checkResult, mlir::Value ok,
171                                   bool inIterWhileLoop = false) {
172   if (!checkResult || !ok)
173     // Either I/O calls do not need to be checked, or the next I/O call is the
174     // first potentially fallable call.
175     return;
176   // A previous I/O call for a statement returned the bool `ok`.  If this call
177   // is in a fir.iterate_while loop, the result must be propagated up to the
178   // loop scope.  That is done in genIoLoop, but it is enabled here.
179   auto whereOp =
180       inIterWhileLoop
181           ? builder.create<fir::WhereOp>(loc, builder.getI1Type(), ok, true)
182           : builder.create<fir::WhereOp>(loc, ok, /*withOtherwise=*/false);
183   if (!insertPt.isSet())
184     insertPt = builder.saveInsertionPoint();
185   builder.setInsertionPointToStart(&whereOp.whereRegion().front());
186 }
187 
188 template <typename D>
189 static void genIoLoop(Fortran::lower::AbstractConverter &converter,
190                       mlir::Value cookie, const D &ioImpliedDo,
191                       bool checkResult, mlir::Value &ok, bool inIterWhileLoop);
192 
193 /// Get the OutputXyz routine to output a value of the given type.
getOutputFunc(mlir::Location loc,Fortran::lower::FirOpBuilder & builder,mlir::Type type)194 static mlir::FuncOp getOutputFunc(mlir::Location loc,
195                                   Fortran::lower::FirOpBuilder &builder,
196                                   mlir::Type type) {
197   if (auto ty = type.dyn_cast<mlir::IntegerType>())
198     return ty.getWidth() == 1
199                ? getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder)
200                : getIORuntimeFunc<mkIOKey(OutputInteger64)>(loc, builder);
201   if (auto ty = type.dyn_cast<mlir::FloatType>())
202     return ty.getWidth() <= 32
203                ? getIORuntimeFunc<mkIOKey(OutputReal32)>(loc, builder)
204                : getIORuntimeFunc<mkIOKey(OutputReal64)>(loc, builder);
205   if (auto ty = type.dyn_cast<fir::CplxType>())
206     return ty.getFKind() <= 4
207                ? getIORuntimeFunc<mkIOKey(OutputComplex32)>(loc, builder)
208                : getIORuntimeFunc<mkIOKey(OutputComplex64)>(loc, builder);
209   if (type.isa<fir::LogicalType>())
210     return getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
211   if (type.isa<fir::BoxType>())
212     return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
213   if (Fortran::lower::CharacterExprHelper::isCharacter(type))
214     return getIORuntimeFunc<mkIOKey(OutputAscii)>(loc, builder);
215   // TODO: handle arrays
216   mlir::emitError(loc, "output for entity type ") << type << " not implemented";
217   return {};
218 }
219 
220 /// Generate a sequence of output data transfer calls.
221 static void
genOutputItemList(Fortran::lower::AbstractConverter & converter,mlir::Value cookie,const std::list<Fortran::parser::OutputItem> & items,mlir::OpBuilder::InsertPoint & insertPt,bool checkResult,mlir::Value & ok,bool inIterWhileLoop)222 genOutputItemList(Fortran::lower::AbstractConverter &converter,
223                   mlir::Value cookie,
224                   const std::list<Fortran::parser::OutputItem> &items,
225                   mlir::OpBuilder::InsertPoint &insertPt, bool checkResult,
226                   mlir::Value &ok, bool inIterWhileLoop) {
227   auto &builder = converter.getFirOpBuilder();
228   for (auto &item : items) {
229     if (const auto &impliedDo = std::get_if<1>(&item.u)) {
230       genIoLoop(converter, cookie, impliedDo->value(), checkResult, ok,
231                 inIterWhileLoop);
232       continue;
233     }
234     auto &pExpr = std::get<Fortran::parser::Expr>(item.u);
235     auto loc = converter.genLocation(pExpr.source);
236     makeNextConditionalOn(builder, loc, insertPt, checkResult, ok,
237                           inIterWhileLoop);
238     auto itemValue =
239         converter.genExprValue(Fortran::semantics::GetExpr(pExpr), loc);
240     auto itemType = itemValue.getType();
241     auto outputFunc = getOutputFunc(loc, builder, itemType);
242     auto argType = outputFunc.getType().getInput(1);
243     llvm::SmallVector<mlir::Value, 3> outputFuncArgs = {cookie};
244     Fortran::lower::CharacterExprHelper helper{builder, loc};
245     if (helper.isCharacter(itemType)) {
246       auto dataLen = helper.materializeCharacter(itemValue);
247       outputFuncArgs.push_back(builder.createConvert(
248           loc, outputFunc.getType().getInput(1), dataLen.first));
249       outputFuncArgs.push_back(builder.createConvert(
250           loc, outputFunc.getType().getInput(2), dataLen.second));
251     } else if (fir::isa_complex(itemType)) {
252       auto parts = Fortran::lower::ComplexExprHelper{builder, loc}.extractParts(
253           itemValue);
254       outputFuncArgs.push_back(parts.first);
255       outputFuncArgs.push_back(parts.second);
256     } else {
257       itemValue = builder.createConvert(loc, argType, itemValue);
258       outputFuncArgs.push_back(itemValue);
259     }
260     ok = builder.create<mlir::CallOp>(loc, outputFunc, outputFuncArgs)
261              .getResult(0);
262   }
263 }
264 
265 /// Get the InputXyz routine to input a value of the given type.
getInputFunc(mlir::Location loc,Fortran::lower::FirOpBuilder & builder,mlir::Type type)266 static mlir::FuncOp getInputFunc(mlir::Location loc,
267                                  Fortran::lower::FirOpBuilder &builder,
268                                  mlir::Type type) {
269   if (auto ty = type.dyn_cast<mlir::IntegerType>())
270     return ty.getWidth() == 1
271                ? getIORuntimeFunc<mkIOKey(InputLogical)>(loc, builder)
272                : getIORuntimeFunc<mkIOKey(InputInteger)>(loc, builder);
273   if (auto ty = type.dyn_cast<mlir::FloatType>())
274     return ty.getWidth() <= 32
275                ? getIORuntimeFunc<mkIOKey(InputReal32)>(loc, builder)
276                : getIORuntimeFunc<mkIOKey(InputReal64)>(loc, builder);
277   if (auto ty = type.dyn_cast<fir::CplxType>())
278     return ty.getFKind() <= 4
279                ? getIORuntimeFunc<mkIOKey(InputReal32)>(loc, builder)
280                : getIORuntimeFunc<mkIOKey(InputReal64)>(loc, builder);
281   if (type.isa<fir::LogicalType>())
282     return getIORuntimeFunc<mkIOKey(InputLogical)>(loc, builder);
283   if (type.isa<fir::BoxType>())
284     return getIORuntimeFunc<mkIOKey(InputDescriptor)>(loc, builder);
285   if (Fortran::lower::CharacterExprHelper::isCharacter(type))
286     return getIORuntimeFunc<mkIOKey(InputAscii)>(loc, builder);
287   // TODO: handle arrays
288   mlir::emitError(loc, "input for entity type ") << type << " not implemented";
289   return {};
290 }
291 
292 /// Generate a sequence of input data transfer calls.
genInputItemList(Fortran::lower::AbstractConverter & converter,mlir::Value cookie,const std::list<Fortran::parser::InputItem> & items,mlir::OpBuilder::InsertPoint & insertPt,bool checkResult,mlir::Value & ok,bool inIterWhileLoop)293 static void genInputItemList(Fortran::lower::AbstractConverter &converter,
294                              mlir::Value cookie,
295                              const std::list<Fortran::parser::InputItem> &items,
296                              mlir::OpBuilder::InsertPoint &insertPt,
297                              bool checkResult, mlir::Value &ok,
298                              bool inIterWhileLoop) {
299   auto &builder = converter.getFirOpBuilder();
300   for (auto &item : items) {
301     if (const auto &impliedDo = std::get_if<1>(&item.u)) {
302       genIoLoop(converter, cookie, impliedDo->value(), checkResult, ok,
303                 inIterWhileLoop);
304       continue;
305     }
306     auto &pVar = std::get<Fortran::parser::Variable>(item.u);
307     auto loc = converter.genLocation(pVar.GetSource());
308     makeNextConditionalOn(builder, loc, insertPt, checkResult, ok,
309                           inIterWhileLoop);
310     auto itemAddr =
311         converter.genExprAddr(Fortran::semantics::GetExpr(pVar), loc);
312     auto itemType = itemAddr.getType().cast<fir::ReferenceType>().getEleTy();
313     auto inputFunc = getInputFunc(loc, builder, itemType);
314     auto argType = inputFunc.getType().getInput(1);
315     auto originalItemAddr = itemAddr;
316     mlir::Type complexPartType;
317     if (itemType.isa<fir::CplxType>())
318       complexPartType = builder.getRefType(
319           Fortran::lower::ComplexExprHelper{builder, loc}.getComplexPartType(
320               itemType));
321     auto complexPartAddr = [&](int index) {
322       return builder.create<fir::CoordinateOp>(
323           loc, complexPartType, originalItemAddr,
324           llvm::SmallVector<mlir::Value, 1>{builder.create<mlir::ConstantOp>(
325               loc, builder.getI32IntegerAttr(index))});
326     };
327     if (complexPartType)
328       itemAddr = complexPartAddr(0); // real part
329     itemAddr = builder.createConvert(loc, argType, itemAddr);
330     llvm::SmallVector<mlir::Value, 3> inputFuncArgs = {cookie, itemAddr};
331     Fortran::lower::CharacterExprHelper helper{builder, loc};
332     if (helper.isCharacter(itemType)) {
333       auto len = helper.materializeCharacter(originalItemAddr).second;
334       inputFuncArgs.push_back(
335           builder.createConvert(loc, inputFunc.getType().getInput(2), len));
336     } else if (itemType.isa<mlir::IntegerType>()) {
337       inputFuncArgs.push_back(builder.create<mlir::ConstantOp>(
338           loc, builder.getI32IntegerAttr(
339                    itemType.cast<mlir::IntegerType>().getWidth() / 8)));
340     }
341     ok = builder.create<mlir::CallOp>(loc, inputFunc, inputFuncArgs)
342              .getResult(0);
343     if (complexPartType) { // imaginary part
344       makeNextConditionalOn(builder, loc, insertPt, checkResult, ok,
345                             inIterWhileLoop);
346       inputFuncArgs = {cookie,
347                        builder.createConvert(loc, argType, complexPartAddr(1))};
348       ok = builder.create<mlir::CallOp>(loc, inputFunc, inputFuncArgs)
349                .getResult(0);
350     }
351   }
352 }
353 
354 /// Generate an io-implied-do loop.
355 template <typename D>
genIoLoop(Fortran::lower::AbstractConverter & converter,mlir::Value cookie,const D & ioImpliedDo,bool checkResult,mlir::Value & ok,bool inIterWhileLoop)356 static void genIoLoop(Fortran::lower::AbstractConverter &converter,
357                       mlir::Value cookie, const D &ioImpliedDo,
358                       bool checkResult, mlir::Value &ok, bool inIterWhileLoop) {
359   mlir::OpBuilder::InsertPoint insertPt;
360   auto &builder = converter.getFirOpBuilder();
361   auto loc = converter.getCurrentLocation();
362   makeNextConditionalOn(builder, loc, insertPt, checkResult, ok,
363                         inIterWhileLoop);
364   auto parentInsertPt = builder.saveInsertionPoint();
365   const auto &itemList = std::get<0>(ioImpliedDo.t);
366   const auto &control = std::get<1>(ioImpliedDo.t);
367   const auto &loopSym = *control.name.thing.thing.symbol;
368   auto loopVar = converter.getSymbolAddress(loopSym);
369   auto genFIRLoopIndex = [&](const Fortran::parser::ScalarIntExpr &expr) {
370     return builder.createConvert(
371         loc, builder.getIndexType(),
372         converter.genExprValue(*Fortran::semantics::GetExpr(expr)));
373   };
374   auto lowerValue = genFIRLoopIndex(control.lower);
375   auto upperValue = genFIRLoopIndex(control.upper);
376   auto stepValue = control.step.has_value()
377                        ? genFIRLoopIndex(*control.step)
378                        : builder.create<mlir::ConstantIndexOp>(loc, 1);
379   auto genItemList = [&](const D &ioImpliedDo, bool inIterWhileLoop) {
380     if constexpr (std::is_same_v<D, Fortran::parser::InputImpliedDo>)
381       genInputItemList(converter, cookie, itemList, insertPt, checkResult, ok,
382                        true);
383     else
384       genOutputItemList(converter, cookie, itemList, insertPt, checkResult, ok,
385                         true);
386   };
387   if (!checkResult) {
388     // No I/O call result checks - the loop is a fir.do_loop op.
389     auto loopOp =
390         builder.create<fir::LoopOp>(loc, lowerValue, upperValue, stepValue);
391     builder.setInsertionPointToStart(loopOp.getBody());
392     auto lcv = builder.createConvert(loc, converter.genType(loopSym),
393                                      loopOp.getInductionVar());
394     builder.create<fir::StoreOp>(loc, lcv, loopVar);
395     insertPt = builder.saveInsertionPoint();
396     genItemList(ioImpliedDo, false);
397     builder.restoreInsertionPoint(parentInsertPt);
398     return;
399   }
400   // Check I/O call results - the loop is a fir.iterate_while op.
401   if (!ok)
402     ok = builder.createIntegerConstant(loc, builder.getI1Type(), 1);
403   fir::IterWhileOp iterWhileOp = builder.create<fir::IterWhileOp>(
404       loc, lowerValue, upperValue, stepValue, ok);
405   builder.setInsertionPointToStart(iterWhileOp.getBody());
406   auto lcv = builder.createConvert(loc, converter.genType(loopSym),
407                                    iterWhileOp.getInductionVar());
408   builder.create<fir::StoreOp>(loc, lcv, loopVar);
409   insertPt = builder.saveInsertionPoint();
410   ok = iterWhileOp.getIterateVar();
411   auto falseValue = builder.createIntegerConstant(loc, builder.getI1Type(), 0);
412   genItemList(ioImpliedDo, true);
413   // Unwind nested I/O call scopes, filling in true and false ResultOp's.
414   for (auto *op = builder.getBlock()->getParentOp(); isa<fir::WhereOp>(op);
415        op = op->getBlock()->getParentOp()) {
416     auto whereOp = dyn_cast<fir::WhereOp>(op);
417     auto *lastOp = &whereOp.whereRegion().front().back();
418     builder.setInsertionPointAfter(lastOp);
419     builder.create<fir::ResultOp>(loc, lastOp->getResult(0)); // runtime result
420     builder.setInsertionPointToStart(&whereOp.otherRegion().front());
421     builder.create<fir::ResultOp>(loc, falseValue); // known false result
422   }
423   builder.restoreInsertionPoint(insertPt);
424   builder.create<fir::ResultOp>(loc, builder.getBlock()->back().getResult(0));
425   ok = iterWhileOp.getResult(0);
426   builder.restoreInsertionPoint(parentInsertPt);
427 }
428 
429 //===----------------------------------------------------------------------===//
430 // Default argument generation.
431 //===----------------------------------------------------------------------===//
432 
getDefaultFilename(Fortran::lower::FirOpBuilder & builder,mlir::Location loc,mlir::Type toType)433 static mlir::Value getDefaultFilename(Fortran::lower::FirOpBuilder &builder,
434                                       mlir::Location loc, mlir::Type toType) {
435   mlir::Value null =
436       builder.create<mlir::ConstantOp>(loc, builder.getI64IntegerAttr(0));
437   return builder.createConvert(loc, toType, null);
438 }
439 
getDefaultLineNo(Fortran::lower::FirOpBuilder & builder,mlir::Location loc,mlir::Type toType)440 static mlir::Value getDefaultLineNo(Fortran::lower::FirOpBuilder &builder,
441                                     mlir::Location loc, mlir::Type toType) {
442   return builder.create<mlir::ConstantOp>(loc,
443                                           builder.getIntegerAttr(toType, 0));
444 }
445 
getDefaultScratch(Fortran::lower::FirOpBuilder & builder,mlir::Location loc,mlir::Type toType)446 static mlir::Value getDefaultScratch(Fortran::lower::FirOpBuilder &builder,
447                                      mlir::Location loc, mlir::Type toType) {
448   mlir::Value null =
449       builder.create<mlir::ConstantOp>(loc, builder.getI64IntegerAttr(0));
450   return builder.createConvert(loc, toType, null);
451 }
452 
getDefaultScratchLen(Fortran::lower::FirOpBuilder & builder,mlir::Location loc,mlir::Type toType)453 static mlir::Value getDefaultScratchLen(Fortran::lower::FirOpBuilder &builder,
454                                         mlir::Location loc, mlir::Type toType) {
455   return builder.create<mlir::ConstantOp>(loc,
456                                           builder.getIntegerAttr(toType, 0));
457 }
458 
459 /// Lower a string literal. Many arguments to the runtime are conveyed as
460 /// Fortran CHARACTER literals.
461 template <typename A>
462 static std::tuple<mlir::Value, mlir::Value, mlir::Value>
lowerStringLit(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & syntax,mlir::Type strTy,mlir::Type lenTy,mlir::Type ty2={})463 lowerStringLit(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
464                const A &syntax, mlir::Type strTy, mlir::Type lenTy,
465                mlir::Type ty2 = {}) {
466   auto &builder = converter.getFirOpBuilder();
467   auto *expr = Fortran::semantics::GetExpr(syntax);
468   auto str = converter.genExprValue(expr, loc);
469   Fortran::lower::CharacterExprHelper helper{builder, loc};
470   auto dataLen = helper.materializeCharacter(str);
471   auto buff = builder.createConvert(loc, strTy, dataLen.first);
472   auto len = builder.createConvert(loc, lenTy, dataLen.second);
473   if (ty2) {
474     auto kindVal = helper.getCharacterKind(str.getType());
475     auto kind = builder.create<mlir::ConstantOp>(
476         loc, builder.getIntegerAttr(ty2, kindVal));
477     return {buff, len, kind};
478   }
479   return {buff, len, mlir::Value{}};
480 }
481 
482 /// Pass the body of the FORMAT statement in as if it were a CHARACTER literal
483 /// constant. NB: This is the prescribed manner in which the front-end passes
484 /// this information to lowering.
485 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)486 lowerSourceTextAsStringLit(Fortran::lower::AbstractConverter &converter,
487                            mlir::Location loc, llvm::StringRef text,
488                            mlir::Type strTy, mlir::Type lenTy) {
489   text = text.drop_front(text.find('('));
490   text = text.take_front(text.rfind(')') + 1);
491   auto &builder = converter.getFirOpBuilder();
492   auto lit = builder.createStringLit(
493       loc, /*FIXME*/ fir::CharacterType::get(builder.getContext(), 1), text);
494   auto data =
495       Fortran::lower::CharacterExprHelper{builder, loc}.materializeCharacter(
496           lit);
497   auto buff = builder.createConvert(loc, strTy, data.first);
498   auto len = builder.createConvert(loc, lenTy, data.second);
499   return {buff, len, mlir::Value{}};
500 }
501 
502 //===----------------------------------------------------------------------===//
503 // Handle I/O statement specifiers.
504 // These are threaded together for a single statement via the passed cookie.
505 //===----------------------------------------------------------------------===//
506 
507 /// Generic to build an integral argument to the runtime.
508 template <typename A, typename B>
genIntIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const B & spec)509 mlir::Value genIntIOOption(Fortran::lower::AbstractConverter &converter,
510                            mlir::Location loc, mlir::Value cookie,
511                            const B &spec) {
512   auto &builder = converter.getFirOpBuilder();
513   mlir::FuncOp ioFunc = getIORuntimeFunc<A>(loc, builder);
514   mlir::FunctionType ioFuncTy = ioFunc.getType();
515   auto expr = converter.genExprValue(Fortran::semantics::GetExpr(spec.v), loc);
516   auto val = builder.createConvert(loc, ioFuncTy.getInput(1), expr);
517   llvm::SmallVector<mlir::Value, 4> ioArgs = {cookie, val};
518   return builder.create<mlir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
519 }
520 
521 /// Generic to build a string argument to the runtime. This passes a CHARACTER
522 /// as a pointer to the buffer and a LEN parameter.
523 template <typename A, typename B>
genCharIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const B & spec)524 mlir::Value genCharIOOption(Fortran::lower::AbstractConverter &converter,
525                             mlir::Location loc, mlir::Value cookie,
526                             const B &spec) {
527   auto &builder = converter.getFirOpBuilder();
528   mlir::FuncOp ioFunc = getIORuntimeFunc<A>(loc, builder);
529   mlir::FunctionType ioFuncTy = ioFunc.getType();
530   auto tup = lowerStringLit(converter, loc, spec, ioFuncTy.getInput(1),
531                             ioFuncTy.getInput(2));
532   llvm::SmallVector<mlir::Value, 4> ioArgs = {cookie, std::get<0>(tup),
533                                               std::get<1>(tup)};
534   return builder.create<mlir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
535 }
536 
537 template <typename A>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const A & spec)538 mlir::Value genIOOption(Fortran::lower::AbstractConverter &converter,
539                         mlir::Location loc, mlir::Value cookie, const A &spec) {
540   // default case: do nothing
541   return {};
542 }
543 
544 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::FileNameExpr & spec)545 mlir::Value genIOOption<Fortran::parser::FileNameExpr>(
546     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
547     mlir::Value cookie, const Fortran::parser::FileNameExpr &spec) {
548   auto &builder = converter.getFirOpBuilder();
549   // has an extra KIND argument
550   auto ioFunc = getIORuntimeFunc<mkIOKey(SetFile)>(loc, builder);
551   mlir::FunctionType ioFuncTy = ioFunc.getType();
552   auto tup = lowerStringLit(converter, loc, spec, ioFuncTy.getInput(1),
553                             ioFuncTy.getInput(2), ioFuncTy.getInput(3));
554   llvm::SmallVector<mlir::Value, 4> ioArgs{cookie, std::get<0>(tup),
555                                            std::get<1>(tup), std::get<2>(tup)};
556   return builder.create<mlir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
557 }
558 
559 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::ConnectSpec::CharExpr & spec)560 mlir::Value genIOOption<Fortran::parser::ConnectSpec::CharExpr>(
561     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
562     mlir::Value cookie, const Fortran::parser::ConnectSpec::CharExpr &spec) {
563   auto &builder = converter.getFirOpBuilder();
564   mlir::FuncOp ioFunc;
565   switch (std::get<Fortran::parser::ConnectSpec::CharExpr::Kind>(spec.t)) {
566   case Fortran::parser::ConnectSpec::CharExpr::Kind::Access:
567     ioFunc = getIORuntimeFunc<mkIOKey(SetAccess)>(loc, builder);
568     break;
569   case Fortran::parser::ConnectSpec::CharExpr::Kind::Action:
570     ioFunc = getIORuntimeFunc<mkIOKey(SetAction)>(loc, builder);
571     break;
572   case Fortran::parser::ConnectSpec::CharExpr::Kind::Asynchronous:
573     ioFunc = getIORuntimeFunc<mkIOKey(SetAsynchronous)>(loc, builder);
574     break;
575   case Fortran::parser::ConnectSpec::CharExpr::Kind::Blank:
576     ioFunc = getIORuntimeFunc<mkIOKey(SetBlank)>(loc, builder);
577     break;
578   case Fortran::parser::ConnectSpec::CharExpr::Kind::Decimal:
579     ioFunc = getIORuntimeFunc<mkIOKey(SetDecimal)>(loc, builder);
580     break;
581   case Fortran::parser::ConnectSpec::CharExpr::Kind::Delim:
582     ioFunc = getIORuntimeFunc<mkIOKey(SetDelim)>(loc, builder);
583     break;
584   case Fortran::parser::ConnectSpec::CharExpr::Kind::Encoding:
585     ioFunc = getIORuntimeFunc<mkIOKey(SetEncoding)>(loc, builder);
586     break;
587   case Fortran::parser::ConnectSpec::CharExpr::Kind::Form:
588     ioFunc = getIORuntimeFunc<mkIOKey(SetForm)>(loc, builder);
589     break;
590   case Fortran::parser::ConnectSpec::CharExpr::Kind::Pad:
591     ioFunc = getIORuntimeFunc<mkIOKey(SetPad)>(loc, builder);
592     break;
593   case Fortran::parser::ConnectSpec::CharExpr::Kind::Position:
594     ioFunc = getIORuntimeFunc<mkIOKey(SetPosition)>(loc, builder);
595     break;
596   case Fortran::parser::ConnectSpec::CharExpr::Kind::Round:
597     ioFunc = getIORuntimeFunc<mkIOKey(SetRound)>(loc, builder);
598     break;
599   case Fortran::parser::ConnectSpec::CharExpr::Kind::Sign:
600     ioFunc = getIORuntimeFunc<mkIOKey(SetSign)>(loc, builder);
601     break;
602   case Fortran::parser::ConnectSpec::CharExpr::Kind::Carriagecontrol:
603     ioFunc = getIORuntimeFunc<mkIOKey(SetCarriagecontrol)>(loc, builder);
604     break;
605   case Fortran::parser::ConnectSpec::CharExpr::Kind::Convert:
606     llvm_unreachable("CONVERT not part of the runtime::io interface");
607   case Fortran::parser::ConnectSpec::CharExpr::Kind::Dispose:
608     llvm_unreachable("DISPOSE not part of the runtime::io interface");
609   }
610   mlir::FunctionType ioFuncTy = ioFunc.getType();
611   auto tup = lowerStringLit(
612       converter, loc, std::get<Fortran::parser::ScalarDefaultCharExpr>(spec.t),
613       ioFuncTy.getInput(1), ioFuncTy.getInput(2));
614   llvm::SmallVector<mlir::Value, 4> ioArgs = {cookie, std::get<0>(tup),
615                                               std::get<1>(tup)};
616   return builder.create<mlir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
617 }
618 
619 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::ConnectSpec::Recl & spec)620 mlir::Value genIOOption<Fortran::parser::ConnectSpec::Recl>(
621     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
622     mlir::Value cookie, const Fortran::parser::ConnectSpec::Recl &spec) {
623   return genIntIOOption<mkIOKey(SetRecl)>(converter, loc, cookie, spec);
624 }
625 
626 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::StatusExpr & spec)627 mlir::Value genIOOption<Fortran::parser::StatusExpr>(
628     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
629     mlir::Value cookie, const Fortran::parser::StatusExpr &spec) {
630   return genCharIOOption<mkIOKey(SetStatus)>(converter, loc, cookie, spec.v);
631 }
632 
633 template <>
634 mlir::Value
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::Name & spec)635 genIOOption<Fortran::parser::Name>(Fortran::lower::AbstractConverter &converter,
636                                    mlir::Location loc, mlir::Value cookie,
637                                    const Fortran::parser::Name &spec) {
638   // namelist
639   llvm_unreachable("not implemented");
640 }
641 
642 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IoControlSpec::CharExpr & spec)643 mlir::Value genIOOption<Fortran::parser::IoControlSpec::CharExpr>(
644     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
645     mlir::Value cookie, const Fortran::parser::IoControlSpec::CharExpr &spec) {
646   auto &builder = converter.getFirOpBuilder();
647   mlir::FuncOp ioFunc;
648   switch (std::get<Fortran::parser::IoControlSpec::CharExpr::Kind>(spec.t)) {
649   case Fortran::parser::IoControlSpec::CharExpr::Kind::Advance:
650     ioFunc = getIORuntimeFunc<mkIOKey(SetAdvance)>(loc, builder);
651     break;
652   case Fortran::parser::IoControlSpec::CharExpr::Kind::Blank:
653     ioFunc = getIORuntimeFunc<mkIOKey(SetBlank)>(loc, builder);
654     break;
655   case Fortran::parser::IoControlSpec::CharExpr::Kind::Decimal:
656     ioFunc = getIORuntimeFunc<mkIOKey(SetDecimal)>(loc, builder);
657     break;
658   case Fortran::parser::IoControlSpec::CharExpr::Kind::Delim:
659     ioFunc = getIORuntimeFunc<mkIOKey(SetDelim)>(loc, builder);
660     break;
661   case Fortran::parser::IoControlSpec::CharExpr::Kind::Pad:
662     ioFunc = getIORuntimeFunc<mkIOKey(SetPad)>(loc, builder);
663     break;
664   case Fortran::parser::IoControlSpec::CharExpr::Kind::Round:
665     ioFunc = getIORuntimeFunc<mkIOKey(SetRound)>(loc, builder);
666     break;
667   case Fortran::parser::IoControlSpec::CharExpr::Kind::Sign:
668     ioFunc = getIORuntimeFunc<mkIOKey(SetSign)>(loc, builder);
669     break;
670   }
671   mlir::FunctionType ioFuncTy = ioFunc.getType();
672   auto tup = lowerStringLit(
673       converter, loc, std::get<Fortran::parser::ScalarDefaultCharExpr>(spec.t),
674       ioFuncTy.getInput(1), ioFuncTy.getInput(2));
675   llvm::SmallVector<mlir::Value, 4> ioArgs = {cookie, std::get<0>(tup),
676                                               std::get<1>(tup)};
677   return builder.create<mlir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
678 }
679 
680 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IoControlSpec::Asynchronous & spec)681 mlir::Value genIOOption<Fortran::parser::IoControlSpec::Asynchronous>(
682     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
683     mlir::Value cookie,
684     const Fortran::parser::IoControlSpec::Asynchronous &spec) {
685   return genCharIOOption<mkIOKey(SetAsynchronous)>(converter, loc, cookie,
686                                                    spec.v);
687 }
688 
689 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IdVariable & spec)690 mlir::Value genIOOption<Fortran::parser::IdVariable>(
691     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
692     mlir::Value cookie, const Fortran::parser::IdVariable &spec) {
693   llvm_unreachable("asynchronous ID not implemented");
694 }
695 
696 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IoControlSpec::Pos & spec)697 mlir::Value genIOOption<Fortran::parser::IoControlSpec::Pos>(
698     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
699     mlir::Value cookie, const Fortran::parser::IoControlSpec::Pos &spec) {
700   return genIntIOOption<mkIOKey(SetPos)>(converter, loc, cookie, spec);
701 }
702 template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IoControlSpec::Rec & spec)703 mlir::Value genIOOption<Fortran::parser::IoControlSpec::Rec>(
704     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
705     mlir::Value cookie, const Fortran::parser::IoControlSpec::Rec &spec) {
706   return genIntIOOption<mkIOKey(SetRec)>(converter, loc, cookie, spec);
707 }
708 
709 //===----------------------------------------------------------------------===//
710 // Gather I/O statement condition specifier information (if any).
711 //===----------------------------------------------------------------------===//
712 
713 template <typename SEEK, typename A>
hasX(const A & list)714 static bool hasX(const A &list) {
715   for (const auto &spec : list)
716     if (std::holds_alternative<SEEK>(spec.u))
717       return true;
718   return false;
719 }
720 
721 template <typename SEEK, typename A>
hasMem(const A & stmt)722 static bool hasMem(const A &stmt) {
723   return hasX<SEEK>(stmt.v);
724 }
725 
726 /// Get the sought expression from the specifier list.
727 template <typename SEEK, typename A>
getExpr(const A & stmt)728 static const Fortran::semantics::SomeExpr *getExpr(const A &stmt) {
729   for (const auto &spec : stmt.v)
730     if (auto *f = std::get_if<SEEK>(&spec.u))
731       return Fortran::semantics::GetExpr(f->v);
732   llvm_unreachable("must have a file unit");
733 }
734 
735 /// For each specifier, build the appropriate call, threading the cookie, and
736 /// returning the insertion point as to the initial context. If there are no
737 /// specifiers, the insertion point is undefined.
738 template <typename A>
739 static mlir::OpBuilder::InsertPoint
threadSpecs(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const A & specList,bool checkResult,mlir::Value & ok)740 threadSpecs(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
741             mlir::Value cookie, const A &specList, bool checkResult,
742             mlir::Value &ok) {
743   auto &builder = converter.getFirOpBuilder();
744   mlir::OpBuilder::InsertPoint insertPt;
745   for (const auto &spec : specList) {
746     makeNextConditionalOn(builder, loc, insertPt, checkResult, ok);
747     ok = std::visit(Fortran::common::visitors{[&](const auto &x) {
748                       return genIOOption(converter, loc, cookie, x);
749                     }},
750                     spec.u);
751   }
752   return insertPt;
753 }
754 
755 template <typename A>
756 static void
genConditionHandlerCall(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const A & specList,ConditionSpecifierInfo & csi)757 genConditionHandlerCall(Fortran::lower::AbstractConverter &converter,
758                         mlir::Location loc, mlir::Value cookie,
759                         const A &specList, ConditionSpecifierInfo &csi) {
760   for (const auto &spec : specList) {
761     std::visit(
762         Fortran::common::visitors{
763             [&](const Fortran::parser::StatVariable &msgVar) {
764               csi.ioStatExpr = Fortran::semantics::GetExpr(msgVar);
765             },
766             [&](const Fortran::parser::MsgVariable &msgVar) {
767               csi.ioMsgExpr = Fortran::semantics::GetExpr(msgVar);
768             },
769             [&](const Fortran::parser::EndLabel &) { csi.hasEnd = true; },
770             [&](const Fortran::parser::EorLabel &) { csi.hasEor = true; },
771             [&](const Fortran::parser::ErrLabel &) { csi.hasErr = true; },
772             [](const auto &) {}},
773         spec.u);
774   }
775   if (!csi.hasAnyConditionSpecifier())
776     return;
777   auto &builder = converter.getFirOpBuilder();
778   mlir::FuncOp enableHandlers =
779       getIORuntimeFunc<mkIOKey(EnableHandlers)>(loc, builder);
780   mlir::Type boolType = enableHandlers.getType().getInput(1);
781   auto boolValue = [&](bool specifierIsPresent) {
782     return builder.create<mlir::ConstantOp>(
783         loc, builder.getIntegerAttr(boolType, specifierIsPresent));
784   };
785   llvm::SmallVector<mlir::Value, 6> ioArgs = {
786       cookie,
787       boolValue(csi.ioStatExpr != nullptr),
788       boolValue(csi.hasErr),
789       boolValue(csi.hasEnd),
790       boolValue(csi.hasEor),
791       boolValue(csi.ioMsgExpr != nullptr)};
792   builder.create<mlir::CallOp>(loc, enableHandlers, ioArgs);
793 }
794 
795 //===----------------------------------------------------------------------===//
796 // Data transfer helpers
797 //===----------------------------------------------------------------------===//
798 
799 template <typename SEEK, typename A>
hasIOControl(const A & stmt)800 static bool hasIOControl(const A &stmt) {
801   return hasX<SEEK>(stmt.controls);
802 }
803 
804 template <typename SEEK, typename A>
getIOControl(const A & stmt)805 static const auto *getIOControl(const A &stmt) {
806   for (const auto &spec : stmt.controls)
807     if (const auto *result = std::get_if<SEEK>(&spec.u))
808       return result;
809   return static_cast<const SEEK *>(nullptr);
810 }
811 
812 /// returns true iff the expression in the parse tree is not really a format but
813 /// rather a namelist variable.
814 template <typename A>
formatIsActuallyNamelist(const A & format)815 static bool formatIsActuallyNamelist(const A &format) {
816   if (auto *e = std::get_if<Fortran::parser::Expr>(&format.u)) {
817     auto *expr = Fortran::semantics::GetExpr(*e);
818     if (const Fortran::semantics::Symbol *y =
819             Fortran::evaluate::UnwrapWholeSymbolDataRef(*expr))
820       return y->has<Fortran::semantics::NamelistDetails>();
821   }
822   return false;
823 }
824 
825 template <typename A>
isDataTransferFormatted(const A & stmt)826 static bool isDataTransferFormatted(const A &stmt) {
827   if (stmt.format)
828     return !formatIsActuallyNamelist(*stmt.format);
829   return hasIOControl<Fortran::parser::Format>(stmt);
830 }
831 template <>
isDataTransferFormatted(const Fortran::parser::PrintStmt &)832 constexpr bool isDataTransferFormatted<Fortran::parser::PrintStmt>(
833     const Fortran::parser::PrintStmt &) {
834   return true; // PRINT is always formatted
835 }
836 
837 template <typename A>
isDataTransferList(const A & stmt)838 static bool isDataTransferList(const A &stmt) {
839   if (stmt.format)
840     return std::holds_alternative<Fortran::parser::Star>(stmt.format->u);
841   if (auto *mem = getIOControl<Fortran::parser::Format>(stmt))
842     return std::holds_alternative<Fortran::parser::Star>(mem->u);
843   return false;
844 }
845 template <>
isDataTransferList(const Fortran::parser::PrintStmt & stmt)846 bool isDataTransferList<Fortran::parser::PrintStmt>(
847     const Fortran::parser::PrintStmt &stmt) {
848   return std::holds_alternative<Fortran::parser::Star>(
849       std::get<Fortran::parser::Format>(stmt.t).u);
850 }
851 
852 template <typename A>
isDataTransferInternal(const A & stmt)853 static bool isDataTransferInternal(const A &stmt) {
854   if (stmt.iounit.has_value())
855     return std::holds_alternative<Fortran::parser::Variable>(stmt.iounit->u);
856   if (auto *unit = getIOControl<Fortran::parser::IoUnit>(stmt))
857     return std::holds_alternative<Fortran::parser::Variable>(unit->u);
858   return false;
859 }
860 template <>
isDataTransferInternal(const Fortran::parser::PrintStmt &)861 constexpr bool isDataTransferInternal<Fortran::parser::PrintStmt>(
862     const Fortran::parser::PrintStmt &) {
863   return false;
864 }
865 
hasNonDefaultCharKind(const Fortran::parser::Variable & var)866 static bool hasNonDefaultCharKind(const Fortran::parser::Variable &var) {
867   // TODO
868   return false;
869 }
870 
871 template <typename A>
isDataTransferInternalNotDefaultKind(const A & stmt)872 static bool isDataTransferInternalNotDefaultKind(const A &stmt) {
873   // same as isDataTransferInternal, but the KIND of the expression is not the
874   // default KIND.
875   if (stmt.iounit.has_value())
876     if (auto *var = std::get_if<Fortran::parser::Variable>(&stmt.iounit->u))
877       return hasNonDefaultCharKind(*var);
878   if (auto *unit = getIOControl<Fortran::parser::IoUnit>(stmt))
879     if (auto *var = std::get_if<Fortran::parser::Variable>(&unit->u))
880       return hasNonDefaultCharKind(*var);
881   return false;
882 }
883 template <>
isDataTransferInternalNotDefaultKind(const Fortran::parser::PrintStmt &)884 constexpr bool isDataTransferInternalNotDefaultKind<Fortran::parser::PrintStmt>(
885     const Fortran::parser::PrintStmt &) {
886   return false;
887 }
888 
889 template <typename A>
isDataTransferAsynchronous(const A & stmt)890 static bool isDataTransferAsynchronous(const A &stmt) {
891   if (auto *asynch =
892           getIOControl<Fortran::parser::IoControlSpec::Asynchronous>(stmt)) {
893     // FIXME: should contain a string of YES or NO
894     llvm_unreachable("asynchronous transfers not implemented in runtime");
895   }
896   return false;
897 }
898 template <>
isDataTransferAsynchronous(const Fortran::parser::PrintStmt &)899 constexpr bool isDataTransferAsynchronous<Fortran::parser::PrintStmt>(
900     const Fortran::parser::PrintStmt &) {
901   return false;
902 }
903 
904 template <typename A>
isDataTransferNamelist(const A & stmt)905 static bool isDataTransferNamelist(const A &stmt) {
906   if (stmt.format)
907     return formatIsActuallyNamelist(*stmt.format);
908   return hasIOControl<Fortran::parser::Name>(stmt);
909 }
910 template <>
isDataTransferNamelist(const Fortran::parser::PrintStmt &)911 constexpr bool isDataTransferNamelist<Fortran::parser::PrintStmt>(
912     const Fortran::parser::PrintStmt &) {
913   return false;
914 }
915 
916 /// Generate a reference to a format string.  There are four cases - a format
917 /// statement label, a character format expression, an integer that holds the
918 /// label of a format statement, and the * case.  The first three are done here.
919 /// The * case is done elsewhere.
920 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::pft::LabelEvalMap & labelMap,Fortran::lower::pft::SymbolLabelMap & assignMap)921 genFormat(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
922           const Fortran::parser::Format &format, mlir::Type strTy,
923           mlir::Type lenTy, Fortran::lower::pft::LabelEvalMap &labelMap,
924           Fortran::lower::pft::SymbolLabelMap &assignMap) {
925   if (const auto *label = std::get_if<Fortran::parser::Label>(&format.u)) {
926     // format statement label
927     auto iter = labelMap.find(*label);
928     assert(iter != labelMap.end() && "FORMAT not found in PROCEDURE");
929     return lowerSourceTextAsStringLit(
930         converter, loc, toStringRef(iter->second->position), strTy, lenTy);
931   }
932   const auto *pExpr = std::get_if<Fortran::parser::Expr>(&format.u);
933   assert(pExpr && "missing format expression");
934   auto e = Fortran::semantics::GetExpr(*pExpr);
935   if (Fortran::semantics::ExprHasTypeCategory(
936           *e, Fortran::common::TypeCategory::Character))
937     // character expression
938     return lowerStringLit(converter, loc, *pExpr, strTy, lenTy);
939   // integer variable containing an ASSIGN label
940   assert(Fortran::semantics::ExprHasTypeCategory(
941       *e, Fortran::common::TypeCategory::Integer));
942   // TODO - implement this
943   llvm::report_fatal_error(
944       "using a variable to reference a FORMAT statement; not implemented yet");
945 }
946 
947 template <typename A>
948 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::pft::LabelEvalMap & labelMap,Fortran::lower::pft::SymbolLabelMap & assignMap)949 getFormat(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
950           const A &stmt, mlir::Type strTy, mlir::Type lenTy,
951           Fortran::lower::pft::LabelEvalMap &labelMap,
952           Fortran::lower::pft::SymbolLabelMap &assignMap) {
953   if (stmt.format && !formatIsActuallyNamelist(*stmt.format))
954     return genFormat(converter, loc, *stmt.format, strTy, lenTy, labelMap,
955                      assignMap);
956   return genFormat(converter, loc, *getIOControl<Fortran::parser::Format>(stmt),
957                    strTy, lenTy, labelMap, assignMap);
958 }
959 template <>
960 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::pft::LabelEvalMap & labelMap,Fortran::lower::pft::SymbolLabelMap & assignMap)961 getFormat<Fortran::parser::PrintStmt>(
962     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
963     const Fortran::parser::PrintStmt &stmt, mlir::Type strTy, mlir::Type lenTy,
964     Fortran::lower::pft::LabelEvalMap &labelMap,
965     Fortran::lower::pft::SymbolLabelMap &assignMap) {
966   return genFormat(converter, loc, std::get<Fortran::parser::Format>(stmt.t),
967                    strTy, lenTy, labelMap, assignMap);
968 }
969 
970 static std::tuple<mlir::Value, mlir::Value, mlir::Value>
genBuffer(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::parser::IoUnit & iounit,mlir::Type strTy,mlir::Type lenTy)971 genBuffer(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
972           const Fortran::parser::IoUnit &iounit, mlir::Type strTy,
973           mlir::Type lenTy) {
974   [[maybe_unused]] auto &var = std::get<Fortran::parser::Variable>(iounit.u);
975   TODO();
976 }
977 template <typename A>
978 std::tuple<mlir::Value, mlir::Value, mlir::Value>
getBuffer(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & stmt,mlir::Type strTy,mlir::Type lenTy)979 getBuffer(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
980           const A &stmt, mlir::Type strTy, mlir::Type lenTy) {
981   if (stmt.iounit)
982     return genBuffer(converter, loc, *stmt.iounit, strTy, lenTy);
983   return genBuffer(converter, loc, *getIOControl<Fortran::parser::IoUnit>(stmt),
984                    strTy, lenTy);
985 }
986 
987 template <typename A>
getDescriptor(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & stmt,mlir::Type toType)988 mlir::Value getDescriptor(Fortran::lower::AbstractConverter &converter,
989                           mlir::Location loc, const A &stmt,
990                           mlir::Type toType) {
991   TODO();
992 }
993 
genIOUnit(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::parser::IoUnit & iounit,mlir::Type ty)994 static mlir::Value genIOUnit(Fortran::lower::AbstractConverter &converter,
995                              mlir::Location loc,
996                              const Fortran::parser::IoUnit &iounit,
997                              mlir::Type ty) {
998   auto &builder = converter.getFirOpBuilder();
999   if (auto *e = std::get_if<Fortran::parser::FileUnitNumber>(&iounit.u)) {
1000     auto ex = converter.genExprValue(Fortran::semantics::GetExpr(*e), loc);
1001     return builder.createConvert(loc, ty, ex);
1002   }
1003   return builder.create<mlir::ConstantOp>(
1004       loc, builder.getIntegerAttr(ty, Fortran::runtime::io::DefaultUnit));
1005 }
1006 
1007 template <typename A>
getIOUnit(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & stmt,mlir::Type ty)1008 mlir::Value getIOUnit(Fortran::lower::AbstractConverter &converter,
1009                       mlir::Location loc, const A &stmt, mlir::Type ty) {
1010   if (stmt.iounit)
1011     return genIOUnit(converter, loc, *stmt.iounit, ty);
1012   return genIOUnit(converter, loc, *getIOControl<Fortran::parser::IoUnit>(stmt),
1013                    ty);
1014 }
1015 
1016 //===----------------------------------------------------------------------===//
1017 // Generators for each I/O statement type.
1018 //===----------------------------------------------------------------------===//
1019 
1020 template <typename K, typename S>
genBasicIOStmt(Fortran::lower::AbstractConverter & converter,const S & stmt)1021 static mlir::Value genBasicIOStmt(Fortran::lower::AbstractConverter &converter,
1022                                   const S &stmt) {
1023   auto &builder = converter.getFirOpBuilder();
1024   auto loc = converter.getCurrentLocation();
1025   auto beginFunc = getIORuntimeFunc<K>(loc, builder);
1026   mlir::FunctionType beginFuncTy = beginFunc.getType();
1027   auto unit = converter.genExprValue(
1028       getExpr<Fortran::parser::FileUnitNumber>(stmt), loc);
1029   auto un = builder.createConvert(loc, beginFuncTy.getInput(0), unit);
1030   auto file = getDefaultFilename(builder, loc, beginFuncTy.getInput(1));
1031   auto line = getDefaultLineNo(builder, loc, beginFuncTy.getInput(2));
1032   llvm::SmallVector<mlir::Value, 4> args{un, file, line};
1033   auto cookie = builder.create<mlir::CallOp>(loc, beginFunc, args).getResult(0);
1034   ConditionSpecifierInfo csi{};
1035   genConditionHandlerCall(converter, loc, cookie, stmt.v, csi);
1036   mlir::Value ok{};
1037   auto insertPt = threadSpecs(converter, loc, cookie, stmt.v,
1038                               csi.hasErrorConditionSpecifier(), ok);
1039   if (insertPt.isSet())
1040     builder.restoreInsertionPoint(insertPt);
1041   return genEndIO(converter, converter.getCurrentLocation(), cookie, csi);
1042 }
1043 
genBackspaceStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::BackspaceStmt & stmt)1044 mlir::Value Fortran::lower::genBackspaceStatement(
1045     Fortran::lower::AbstractConverter &converter,
1046     const Fortran::parser::BackspaceStmt &stmt) {
1047   return genBasicIOStmt<mkIOKey(BeginBackspace)>(converter, stmt);
1048 }
1049 
genEndfileStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::EndfileStmt & stmt)1050 mlir::Value Fortran::lower::genEndfileStatement(
1051     Fortran::lower::AbstractConverter &converter,
1052     const Fortran::parser::EndfileStmt &stmt) {
1053   return genBasicIOStmt<mkIOKey(BeginEndfile)>(converter, stmt);
1054 }
1055 
1056 mlir::Value
genFlushStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::FlushStmt & stmt)1057 Fortran::lower::genFlushStatement(Fortran::lower::AbstractConverter &converter,
1058                                   const Fortran::parser::FlushStmt &stmt) {
1059   return genBasicIOStmt<mkIOKey(BeginFlush)>(converter, stmt);
1060 }
1061 
1062 mlir::Value
genRewindStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::RewindStmt & stmt)1063 Fortran::lower::genRewindStatement(Fortran::lower::AbstractConverter &converter,
1064                                    const Fortran::parser::RewindStmt &stmt) {
1065   return genBasicIOStmt<mkIOKey(BeginRewind)>(converter, stmt);
1066 }
1067 
1068 mlir::Value
genOpenStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::OpenStmt & stmt)1069 Fortran::lower::genOpenStatement(Fortran::lower::AbstractConverter &converter,
1070                                  const Fortran::parser::OpenStmt &stmt) {
1071   auto &builder = converter.getFirOpBuilder();
1072   mlir::FuncOp beginFunc;
1073   llvm::SmallVector<mlir::Value, 4> beginArgs;
1074   auto loc = converter.getCurrentLocation();
1075   if (hasMem<Fortran::parser::FileUnitNumber>(stmt)) {
1076     beginFunc = getIORuntimeFunc<mkIOKey(BeginOpenUnit)>(loc, builder);
1077     mlir::FunctionType beginFuncTy = beginFunc.getType();
1078     auto unit = converter.genExprValue(
1079         getExpr<Fortran::parser::FileUnitNumber>(stmt), loc);
1080     beginArgs.push_back(
1081         builder.createConvert(loc, beginFuncTy.getInput(0), unit));
1082     beginArgs.push_back(
1083         getDefaultFilename(builder, loc, beginFuncTy.getInput(1)));
1084     beginArgs.push_back(
1085         getDefaultLineNo(builder, loc, beginFuncTy.getInput(2)));
1086   } else {
1087     assert(hasMem<Fortran::parser::ConnectSpec::Newunit>(stmt));
1088     beginFunc = getIORuntimeFunc<mkIOKey(BeginOpenNewUnit)>(loc, builder);
1089     mlir::FunctionType beginFuncTy = beginFunc.getType();
1090     beginArgs.push_back(
1091         getDefaultFilename(builder, loc, beginFuncTy.getInput(0)));
1092     beginArgs.push_back(
1093         getDefaultLineNo(builder, loc, beginFuncTy.getInput(1)));
1094   }
1095   auto cookie =
1096       builder.create<mlir::CallOp>(loc, beginFunc, beginArgs).getResult(0);
1097   ConditionSpecifierInfo csi{};
1098   genConditionHandlerCall(converter, loc, cookie, stmt.v, csi);
1099   mlir::Value ok{};
1100   auto insertPt = threadSpecs(converter, loc, cookie, stmt.v,
1101                               csi.hasErrorConditionSpecifier(), ok);
1102   if (insertPt.isSet())
1103     builder.restoreInsertionPoint(insertPt);
1104   return genEndIO(converter, loc, cookie, csi);
1105 }
1106 
1107 mlir::Value
genCloseStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::CloseStmt & stmt)1108 Fortran::lower::genCloseStatement(Fortran::lower::AbstractConverter &converter,
1109                                   const Fortran::parser::CloseStmt &stmt) {
1110   return genBasicIOStmt<mkIOKey(BeginClose)>(converter, stmt);
1111 }
1112 
1113 mlir::Value
genWaitStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::WaitStmt & stmt)1114 Fortran::lower::genWaitStatement(Fortran::lower::AbstractConverter &converter,
1115                                  const Fortran::parser::WaitStmt &stmt) {
1116   auto &builder = converter.getFirOpBuilder();
1117   auto loc = converter.getCurrentLocation();
1118   bool hasId = hasMem<Fortran::parser::IdExpr>(stmt);
1119   mlir::FuncOp beginFunc =
1120       hasId ? getIORuntimeFunc<mkIOKey(BeginWait)>(loc, builder)
1121             : getIORuntimeFunc<mkIOKey(BeginWaitAll)>(loc, builder);
1122   mlir::FunctionType beginFuncTy = beginFunc.getType();
1123   auto unit = converter.genExprValue(
1124       getExpr<Fortran::parser::FileUnitNumber>(stmt), loc);
1125   auto un = builder.createConvert(loc, beginFuncTy.getInput(0), unit);
1126   llvm::SmallVector<mlir::Value, 4> args{un};
1127   if (hasId) {
1128     auto id =
1129         converter.genExprValue(getExpr<Fortran::parser::IdExpr>(stmt), loc);
1130     args.push_back(builder.createConvert(loc, beginFuncTy.getInput(1), id));
1131   }
1132   auto cookie = builder.create<mlir::CallOp>(loc, beginFunc, args).getResult(0);
1133   ConditionSpecifierInfo csi{};
1134   genConditionHandlerCall(converter, loc, cookie, stmt.v, csi);
1135   return genEndIO(converter, converter.getCurrentLocation(), cookie, csi);
1136 }
1137 
1138 //===----------------------------------------------------------------------===//
1139 // Data transfer statements.
1140 //
1141 // There are several dimensions to the API with regard to data transfer
1142 // statements that need to be considered.
1143 //
1144 //   - input (READ) vs. output (WRITE, PRINT)
1145 //   - formatted vs. list vs. unformatted
1146 //   - synchronous vs. asynchronous
1147 //   - namelist vs. list
1148 //   - external vs. internal + default KIND vs. internal + other KIND
1149 //===----------------------------------------------------------------------===//
1150 
1151 // Determine the correct BeginXyz{In|Out}put api to invoke.
1152 template <bool isInput>
getBeginDataTransfer(mlir::Location loc,FirOpBuilder & builder,bool isFormatted,bool isList,bool isIntern,bool isOtherIntern,bool isAsynch,bool isNml)1153 mlir::FuncOp getBeginDataTransfer(mlir::Location loc, FirOpBuilder &builder,
1154                                   bool isFormatted, bool isList, bool isIntern,
1155                                   bool isOtherIntern, bool isAsynch,
1156                                   bool isNml) {
1157   if constexpr (isInput) {
1158     if (isAsynch)
1159       return getIORuntimeFunc<mkIOKey(BeginAsynchronousInput)>(loc, builder);
1160     if (isFormatted) {
1161       if (isIntern) {
1162         if (isNml)
1163           return getIORuntimeFunc<mkIOKey(BeginInternalNamelistInput)>(loc,
1164                                                                        builder);
1165         if (isOtherIntern) {
1166           if (isList)
1167             return getIORuntimeFunc<mkIOKey(BeginInternalArrayListInput)>(
1168                 loc, builder);
1169           return getIORuntimeFunc<mkIOKey(BeginInternalArrayFormattedInput)>(
1170               loc, builder);
1171         }
1172         if (isList)
1173           return getIORuntimeFunc<mkIOKey(BeginInternalListInput)>(loc,
1174                                                                    builder);
1175         return getIORuntimeFunc<mkIOKey(BeginInternalFormattedInput)>(loc,
1176                                                                       builder);
1177       }
1178       if (isNml)
1179         return getIORuntimeFunc<mkIOKey(BeginExternalNamelistInput)>(loc,
1180                                                                      builder);
1181       if (isList)
1182         return getIORuntimeFunc<mkIOKey(BeginExternalListInput)>(loc, builder);
1183       return getIORuntimeFunc<mkIOKey(BeginExternalFormattedInput)>(loc,
1184                                                                     builder);
1185     }
1186     return getIORuntimeFunc<mkIOKey(BeginUnformattedInput)>(loc, builder);
1187   } else {
1188     if (isAsynch)
1189       return getIORuntimeFunc<mkIOKey(BeginAsynchronousOutput)>(loc, builder);
1190     if (isFormatted) {
1191       if (isIntern) {
1192         if (isNml)
1193           return getIORuntimeFunc<mkIOKey(BeginInternalNamelistOutput)>(
1194               loc, builder);
1195         if (isOtherIntern) {
1196           if (isList)
1197             return getIORuntimeFunc<mkIOKey(BeginInternalArrayListOutput)>(
1198                 loc, builder);
1199           return getIORuntimeFunc<mkIOKey(BeginInternalArrayFormattedOutput)>(
1200               loc, builder);
1201         }
1202         if (isList)
1203           return getIORuntimeFunc<mkIOKey(BeginInternalListOutput)>(loc,
1204                                                                     builder);
1205         return getIORuntimeFunc<mkIOKey(BeginInternalFormattedOutput)>(loc,
1206                                                                        builder);
1207       }
1208       if (isNml)
1209         return getIORuntimeFunc<mkIOKey(BeginExternalNamelistOutput)>(loc,
1210                                                                       builder);
1211       if (isList)
1212         return getIORuntimeFunc<mkIOKey(BeginExternalListOutput)>(loc, builder);
1213       return getIORuntimeFunc<mkIOKey(BeginExternalFormattedOutput)>(loc,
1214                                                                      builder);
1215     }
1216     return getIORuntimeFunc<mkIOKey(BeginUnformattedOutput)>(loc, builder);
1217   }
1218 }
1219 
1220 /// Generate the arguments of a BeginXyz call.
1221 template <bool hasIOCtrl, typename A>
genBeginCallArguments(llvm::SmallVector<mlir::Value,8> & ioArgs,Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & stmt,mlir::FunctionType ioFuncTy,bool isFormatted,bool isList,bool isIntern,bool isOtherIntern,bool isAsynch,bool isNml,Fortran::lower::pft::LabelEvalMap & labelMap,Fortran::lower::pft::SymbolLabelMap & assignMap)1222 void genBeginCallArguments(llvm::SmallVector<mlir::Value, 8> &ioArgs,
1223                            Fortran::lower::AbstractConverter &converter,
1224                            mlir::Location loc, const A &stmt,
1225                            mlir::FunctionType ioFuncTy, bool isFormatted,
1226                            bool isList, bool isIntern, bool isOtherIntern,
1227                            bool isAsynch, bool isNml,
1228                            Fortran::lower::pft::LabelEvalMap &labelMap,
1229                            Fortran::lower::pft::SymbolLabelMap &assignMap) {
1230   auto &builder = converter.getFirOpBuilder();
1231   if constexpr (hasIOCtrl) {
1232     // READ/WRITE cases have a wide variety of argument permutations
1233     if (isAsynch || !isFormatted) {
1234       // unit (always first), ...
1235       ioArgs.push_back(
1236           getIOUnit(converter, loc, stmt, ioFuncTy.getInput(ioArgs.size())));
1237       if (isAsynch) {
1238         // unknown-thingy, [buff, LEN]
1239         llvm_unreachable("not implemented");
1240       }
1241       return;
1242     }
1243     assert(isFormatted && "formatted data transfer");
1244     if (!isIntern) {
1245       if (isNml) {
1246         // namelist group, ...
1247         llvm_unreachable("not implemented");
1248       } else if (!isList) {
1249         // | [format, LEN], ...
1250         auto pair = getFormat(
1251             converter, loc, stmt, ioFuncTy.getInput(ioArgs.size()),
1252             ioFuncTy.getInput(ioArgs.size() + 1), labelMap, assignMap);
1253         ioArgs.push_back(std::get<0>(pair));
1254         ioArgs.push_back(std::get<1>(pair));
1255       }
1256       // unit (always last)
1257       ioArgs.push_back(
1258           getIOUnit(converter, loc, stmt, ioFuncTy.getInput(ioArgs.size())));
1259       return;
1260     }
1261     assert(isIntern && "internal data transfer");
1262     if (isNml || isOtherIntern) {
1263       // descriptor, ...
1264       ioArgs.push_back(getDescriptor(converter, loc, stmt,
1265                                      ioFuncTy.getInput(ioArgs.size())));
1266       if (isNml) {
1267         // namelist group, ...
1268         llvm_unreachable("not implemented");
1269       } else if (isOtherIntern && !isList) {
1270         // | [format, LEN], ...
1271         auto pair = getFormat(
1272             converter, loc, stmt, ioFuncTy.getInput(ioArgs.size()),
1273             ioFuncTy.getInput(ioArgs.size() + 1), labelMap, assignMap);
1274         ioArgs.push_back(std::get<0>(pair));
1275         ioArgs.push_back(std::get<1>(pair));
1276       }
1277     } else {
1278       // | [buff, LEN], ...
1279       auto pair =
1280           getBuffer(converter, loc, stmt, ioFuncTy.getInput(ioArgs.size()),
1281                     ioFuncTy.getInput(ioArgs.size() + 1));
1282       ioArgs.push_back(std::get<0>(pair));
1283       ioArgs.push_back(std::get<1>(pair));
1284       if (!isList) {
1285         // [format, LEN], ...
1286         auto pair = getFormat(
1287             converter, loc, stmt, ioFuncTy.getInput(ioArgs.size()),
1288             ioFuncTy.getInput(ioArgs.size() + 1), labelMap, assignMap);
1289         ioArgs.push_back(std::get<0>(pair));
1290         ioArgs.push_back(std::get<1>(pair));
1291       }
1292     }
1293     // [scratch, LEN] (always last)
1294     ioArgs.push_back(
1295         getDefaultScratch(builder, loc, ioFuncTy.getInput(ioArgs.size())));
1296     ioArgs.push_back(
1297         getDefaultScratchLen(builder, loc, ioFuncTy.getInput(ioArgs.size())));
1298   } else {
1299     if (!isList) {
1300       // [format, LEN], ...
1301       auto pair =
1302           getFormat(converter, loc, stmt, ioFuncTy.getInput(ioArgs.size()),
1303                     ioFuncTy.getInput(ioArgs.size() + 1), labelMap, assignMap);
1304       ioArgs.push_back(std::get<0>(pair));
1305       ioArgs.push_back(std::get<1>(pair));
1306     }
1307     // unit (always last)
1308     ioArgs.push_back(builder.create<mlir::ConstantOp>(
1309         loc, builder.getIntegerAttr(ioFuncTy.getInput(ioArgs.size()),
1310                                     Fortran::runtime::io::DefaultUnit)));
1311   }
1312 }
1313 
1314 template <bool isInput, bool hasIOCtrl = true, typename A>
1315 static mlir::Value
genDataTransferStmt(Fortran::lower::AbstractConverter & converter,const A & stmt,Fortran::lower::pft::LabelEvalMap & labelMap,Fortran::lower::pft::SymbolLabelMap & assignMap)1316 genDataTransferStmt(Fortran::lower::AbstractConverter &converter, const A &stmt,
1317                     Fortran::lower::pft::LabelEvalMap &labelMap,
1318                     Fortran::lower::pft::SymbolLabelMap &assignMap) {
1319   auto &builder = converter.getFirOpBuilder();
1320   auto loc = converter.getCurrentLocation();
1321   const bool isFormatted = isDataTransferFormatted(stmt);
1322   const bool isList = isFormatted ? isDataTransferList(stmt) : false;
1323   const bool isIntern = isDataTransferInternal(stmt);
1324   const bool isOtherIntern =
1325       isIntern ? isDataTransferInternalNotDefaultKind(stmt) : false;
1326   const bool isAsynch = isDataTransferAsynchronous(stmt);
1327   const bool isNml = isDataTransferNamelist(stmt);
1328 
1329   // Determine which BeginXyz call to make.
1330   mlir::FuncOp ioFunc =
1331       getBeginDataTransfer<isInput>(loc, builder, isFormatted, isList, isIntern,
1332                                     isOtherIntern, isAsynch, isNml);
1333   mlir::FunctionType ioFuncTy = ioFunc.getType();
1334 
1335   // Append BeginXyz call arguments.  File name and line number are always last.
1336   llvm::SmallVector<mlir::Value, 8> ioArgs;
1337   genBeginCallArguments<hasIOCtrl>(ioArgs, converter, loc, stmt, ioFuncTy,
1338                                    isFormatted, isList, isIntern, isOtherIntern,
1339                                    isAsynch, isNml, labelMap, assignMap);
1340   ioArgs.push_back(
1341       getDefaultFilename(builder, loc, ioFuncTy.getInput(ioArgs.size())));
1342   ioArgs.push_back(
1343       getDefaultLineNo(builder, loc, ioFuncTy.getInput(ioArgs.size())));
1344 
1345   // Arguments are done; call the BeginXyz function.
1346   mlir::Value cookie =
1347       builder.create<mlir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
1348 
1349   // Generate an EnableHandlers call and remaining specifier calls.
1350   ConditionSpecifierInfo csi;
1351   mlir::OpBuilder::InsertPoint insertPt;
1352   mlir::Value ok;
1353   if constexpr (hasIOCtrl) {
1354     genConditionHandlerCall(converter, loc, cookie, stmt.controls, csi);
1355     insertPt = threadSpecs(converter, loc, cookie, stmt.controls,
1356                            csi.hasErrorConditionSpecifier(), ok);
1357   }
1358 
1359   // Generate data transfer list calls.
1360   if constexpr (isInput) // ReadStmt
1361     genInputItemList(converter, cookie, stmt.items, insertPt,
1362                      csi.hasTransferConditionSpecifier(), ok, false);
1363   else if constexpr (std::is_same_v<A, Fortran::parser::PrintStmt>)
1364     genOutputItemList(converter, cookie, std::get<1>(stmt.t), insertPt,
1365                       csi.hasTransferConditionSpecifier(), ok, false);
1366   else // WriteStmt
1367     genOutputItemList(converter, cookie, stmt.items, insertPt,
1368                       csi.hasTransferConditionSpecifier(), ok, false);
1369 
1370   // Generate end statement call/s.
1371   if (insertPt.isSet())
1372     builder.restoreInsertionPoint(insertPt);
1373   return genEndIO(converter, loc, cookie, csi);
1374 }
1375 
genPrintStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::PrintStmt & stmt,Fortran::lower::pft::LabelEvalMap & labelMap,Fortran::lower::pft::SymbolLabelMap & assignMap)1376 void Fortran::lower::genPrintStatement(
1377     Fortran::lower::AbstractConverter &converter,
1378     const Fortran::parser::PrintStmt &stmt,
1379     Fortran::lower::pft::LabelEvalMap &labelMap,
1380     Fortran::lower::pft::SymbolLabelMap &assignMap) {
1381   // PRINT does not take an io-control-spec. It only has a format specifier, so
1382   // it is a simplified case of WRITE.
1383   genDataTransferStmt</*isInput=*/false, /*ioCtrl=*/false>(converter, stmt,
1384                                                            labelMap, assignMap);
1385 }
1386 
genWriteStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::WriteStmt & stmt,Fortran::lower::pft::LabelEvalMap & labelMap,Fortran::lower::pft::SymbolLabelMap & assignMap)1387 mlir::Value Fortran::lower::genWriteStatement(
1388     Fortran::lower::AbstractConverter &converter,
1389     const Fortran::parser::WriteStmt &stmt,
1390     Fortran::lower::pft::LabelEvalMap &labelMap,
1391     Fortran::lower::pft::SymbolLabelMap &assignMap) {
1392   return genDataTransferStmt</*isInput=*/false>(converter, stmt, labelMap,
1393                                                 assignMap);
1394 }
1395 
genReadStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::ReadStmt & stmt,Fortran::lower::pft::LabelEvalMap & labelMap,Fortran::lower::pft::SymbolLabelMap & assignMap)1396 mlir::Value Fortran::lower::genReadStatement(
1397     Fortran::lower::AbstractConverter &converter,
1398     const Fortran::parser::ReadStmt &stmt,
1399     Fortran::lower::pft::LabelEvalMap &labelMap,
1400     Fortran::lower::pft::SymbolLabelMap &assignMap) {
1401   return genDataTransferStmt</*isInput=*/true>(converter, stmt, labelMap,
1402                                                assignMap);
1403 }
1404 
1405 /// Get the file expression from the inquire spec list. Also return if the
1406 /// expression is a file name.
1407 static std::pair<const Fortran::semantics::SomeExpr *, bool>
getInquireFileExpr(const std::list<Fortran::parser::InquireSpec> * stmt)1408 getInquireFileExpr(const std::list<Fortran::parser::InquireSpec> *stmt) {
1409   if (!stmt)
1410     return {nullptr, false};
1411   for (const auto &spec : *stmt) {
1412     if (auto *f = std::get_if<Fortran::parser::FileUnitNumber>(&spec.u))
1413       return {Fortran::semantics::GetExpr(*f), false};
1414     if (auto *f = std::get_if<Fortran::parser::FileNameExpr>(&spec.u))
1415       return {Fortran::semantics::GetExpr(*f), true};
1416   }
1417   // semantics should have already caught this condition
1418   llvm_unreachable("inquire spec must have a file");
1419 }
1420 
genInquireStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::InquireStmt & stmt)1421 mlir::Value Fortran::lower::genInquireStatement(
1422     Fortran::lower::AbstractConverter &converter,
1423     const Fortran::parser::InquireStmt &stmt) {
1424   auto &builder = converter.getFirOpBuilder();
1425   auto loc = converter.getCurrentLocation();
1426   mlir::FuncOp beginFunc;
1427   mlir::Value cookie;
1428   ConditionSpecifierInfo csi{};
1429   const auto *list =
1430       std::get_if<std::list<Fortran::parser::InquireSpec>>(&stmt.u);
1431   auto exprPair = getInquireFileExpr(list);
1432   auto inquireFileUnit = [&]() -> bool {
1433     return exprPair.first && !exprPair.second;
1434   };
1435   auto inquireFileName = [&]() -> bool {
1436     return exprPair.first && exprPair.second;
1437   };
1438 
1439   // Determine which BeginInquire call to make.
1440   if (inquireFileUnit()) {
1441     // File unit call.
1442     beginFunc = getIORuntimeFunc<mkIOKey(BeginInquireUnit)>(loc, builder);
1443     mlir::FunctionType beginFuncTy = beginFunc.getType();
1444     auto unit = converter.genExprValue(exprPair.first, loc);
1445     auto un = builder.createConvert(loc, beginFuncTy.getInput(0), unit);
1446     auto file = getDefaultFilename(builder, loc, beginFuncTy.getInput(1));
1447     auto line = getDefaultLineNo(builder, loc, beginFuncTy.getInput(2));
1448     llvm::SmallVector<mlir::Value, 4> beginArgs{un, file, line};
1449     cookie =
1450         builder.create<mlir::CallOp>(loc, beginFunc, beginArgs).getResult(0);
1451     // Handle remaining arguments in specifier list.
1452     genConditionHandlerCall(converter, loc, cookie, *list, csi);
1453   } else if (inquireFileName()) {
1454     // Filename call.
1455     beginFunc = getIORuntimeFunc<mkIOKey(BeginInquireFile)>(loc, builder);
1456     mlir::FunctionType beginFuncTy = beginFunc.getType();
1457     auto file = converter.genExprValue(exprPair.first, loc);
1458     // Helper to query [BUFFER, LEN].
1459     Fortran::lower::CharacterExprHelper helper(builder, loc);
1460     auto dataLen = helper.materializeCharacter(file);
1461     auto buff =
1462         builder.createConvert(loc, beginFuncTy.getInput(0), dataLen.first);
1463     auto len =
1464         builder.createConvert(loc, beginFuncTy.getInput(1), dataLen.second);
1465     auto kindInt = helper.getCharacterKind(file.getType());
1466     mlir::Value kindValue =
1467         builder.createIntegerConstant(loc, beginFuncTy.getInput(2), kindInt);
1468     auto sourceFile = getDefaultFilename(builder, loc, beginFuncTy.getInput(3));
1469     auto line = getDefaultLineNo(builder, loc, beginFuncTy.getInput(4));
1470     llvm::SmallVector<mlir::Value, 5> beginArgs = {
1471         buff, len, kindValue, sourceFile, line,
1472     };
1473     cookie =
1474         builder.create<mlir::CallOp>(loc, beginFunc, beginArgs).getResult(0);
1475     // Handle remaining arguments in specifier list.
1476     genConditionHandlerCall(converter, loc, cookie, *list, csi);
1477   } else {
1478     // Io length call.
1479     const auto *ioLength =
1480         std::get_if<Fortran::parser::InquireStmt::Iolength>(&stmt.u);
1481     assert(ioLength && "must have an io length");
1482     beginFunc = getIORuntimeFunc<mkIOKey(BeginInquireIoLength)>(loc, builder);
1483     mlir::FunctionType beginFuncTy = beginFunc.getType();
1484     auto file = getDefaultFilename(builder, loc, beginFuncTy.getInput(0));
1485     auto line = getDefaultLineNo(builder, loc, beginFuncTy.getInput(1));
1486     llvm::SmallVector<mlir::Value, 4> beginArgs{file, line};
1487     cookie =
1488         builder.create<mlir::CallOp>(loc, beginFunc, beginArgs).getResult(0);
1489     // Handle remaining arguments in output list.
1490     genConditionHandlerCall(
1491         converter, loc, cookie,
1492         std::get<std::list<Fortran::parser::OutputItem>>(ioLength->t), csi);
1493   }
1494   // Generate end statement call.
1495   return genEndIO(converter, loc, cookie, csi);
1496 }
1497