1b3eb0e11SValentin Clement //===-- CustomIntrinsicCall.cpp -------------------------------------------===//
2b3eb0e11SValentin Clement //
3b3eb0e11SValentin Clement // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b3eb0e11SValentin Clement // See https://llvm.org/LICENSE.txt for license information.
5b3eb0e11SValentin Clement // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b3eb0e11SValentin Clement //
7b3eb0e11SValentin Clement //===----------------------------------------------------------------------===//
8b3eb0e11SValentin Clement //
9b3eb0e11SValentin Clement // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10b3eb0e11SValentin Clement //
11b3eb0e11SValentin Clement //===----------------------------------------------------------------------===//
12b3eb0e11SValentin Clement 
13b3eb0e11SValentin Clement #include "flang/Lower/CustomIntrinsicCall.h"
14b3eb0e11SValentin Clement #include "flang/Evaluate/expression.h"
15b3eb0e11SValentin Clement #include "flang/Evaluate/fold.h"
16b3eb0e11SValentin Clement #include "flang/Evaluate/tools.h"
17b3eb0e11SValentin Clement #include "flang/Lower/IntrinsicCall.h"
185b66cc10SValentin Clement #include "flang/Optimizer/Builder/Todo.h"
19b3eb0e11SValentin Clement 
20b3eb0e11SValentin Clement /// Is this a call to MIN or MAX intrinsic with arguments that may be absent at
21b3eb0e11SValentin Clement /// runtime? This is a special case because MIN and MAX can have any number of
22b3eb0e11SValentin Clement /// arguments.
isMinOrMaxWithDynamicallyOptionalArg(llvm::StringRef name,const Fortran::evaluate::ProcedureRef & procRef,Fortran::evaluate::FoldingContext & foldingContex)23b3eb0e11SValentin Clement static bool isMinOrMaxWithDynamicallyOptionalArg(
24b3eb0e11SValentin Clement     llvm::StringRef name, const Fortran::evaluate::ProcedureRef &procRef,
25b3eb0e11SValentin Clement     Fortran::evaluate::FoldingContext &foldingContex) {
26b3eb0e11SValentin Clement   if (name != "min" && name != "max")
27b3eb0e11SValentin Clement     return false;
28b3eb0e11SValentin Clement   const auto &args = procRef.arguments();
29b3eb0e11SValentin Clement   std::size_t argSize = args.size();
30b3eb0e11SValentin Clement   if (argSize <= 2)
31b3eb0e11SValentin Clement     return false;
32b3eb0e11SValentin Clement   for (std::size_t i = 2; i < argSize; ++i) {
33b3eb0e11SValentin Clement     if (auto *expr =
34b3eb0e11SValentin Clement             Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(args[i]))
35b3eb0e11SValentin Clement       if (Fortran::evaluate::MayBePassedAsAbsentOptional(*expr, foldingContex))
36b3eb0e11SValentin Clement         return true;
37b3eb0e11SValentin Clement   }
38b3eb0e11SValentin Clement   return false;
39b3eb0e11SValentin Clement }
40b3eb0e11SValentin Clement 
41b3eb0e11SValentin Clement /// Is this a call to ISHFTC intrinsic with a SIZE argument that may be absent
42b3eb0e11SValentin Clement /// at runtime? This is a special case because the SIZE value to be applied
43b3eb0e11SValentin Clement /// when absent is not zero.
isIshftcWithDynamicallyOptionalArg(llvm::StringRef name,const Fortran::evaluate::ProcedureRef & procRef,Fortran::evaluate::FoldingContext & foldingContex)44b3eb0e11SValentin Clement static bool isIshftcWithDynamicallyOptionalArg(
45b3eb0e11SValentin Clement     llvm::StringRef name, const Fortran::evaluate::ProcedureRef &procRef,
46b3eb0e11SValentin Clement     Fortran::evaluate::FoldingContext &foldingContex) {
47b3eb0e11SValentin Clement   if (name != "ishftc" || procRef.arguments().size() < 3)
48b3eb0e11SValentin Clement     return false;
49b3eb0e11SValentin Clement   auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(
50b3eb0e11SValentin Clement       procRef.arguments()[2]);
51b3eb0e11SValentin Clement   return expr &&
52b3eb0e11SValentin Clement          Fortran::evaluate::MayBePassedAsAbsentOptional(*expr, foldingContex);
53b3eb0e11SValentin Clement }
54b3eb0e11SValentin Clement 
5570ade047Svdonaldson /// Is this a call to the RANDOM_SEED intrinsic with arguments that may be
5670ade047Svdonaldson /// absent at runtime? This is a special case because that aspect cannot
57b3eb0e11SValentin Clement /// be delegated to the runtime via a null fir.box or address given the current
58b3eb0e11SValentin Clement /// runtime entry point.
isRandomSeedWithDynamicallyOptionalArg(llvm::StringRef name,const Fortran::evaluate::ProcedureRef & procRef,Fortran::evaluate::FoldingContext & foldingContex)5970ade047Svdonaldson static bool isRandomSeedWithDynamicallyOptionalArg(
60b3eb0e11SValentin Clement     llvm::StringRef name, const Fortran::evaluate::ProcedureRef &procRef,
61b3eb0e11SValentin Clement     Fortran::evaluate::FoldingContext &foldingContex) {
6270ade047Svdonaldson   if (name != "random_seed")
63b3eb0e11SValentin Clement     return false;
64b3eb0e11SValentin Clement   for (const auto &arg : procRef.arguments()) {
65b3eb0e11SValentin Clement     auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
66b3eb0e11SValentin Clement     if (expr &&
67b3eb0e11SValentin Clement         Fortran::evaluate::MayBePassedAsAbsentOptional(*expr, foldingContex))
68b3eb0e11SValentin Clement       return true;
69b3eb0e11SValentin Clement   }
70b3eb0e11SValentin Clement   return false;
71b3eb0e11SValentin Clement }
72b3eb0e11SValentin Clement 
intrinsicRequiresCustomOptionalHandling(const Fortran::evaluate::ProcedureRef & procRef,const Fortran::evaluate::SpecificIntrinsic & intrinsic,AbstractConverter & converter)73b3eb0e11SValentin Clement bool Fortran::lower::intrinsicRequiresCustomOptionalHandling(
74b3eb0e11SValentin Clement     const Fortran::evaluate::ProcedureRef &procRef,
75b3eb0e11SValentin Clement     const Fortran::evaluate::SpecificIntrinsic &intrinsic,
76b3eb0e11SValentin Clement     AbstractConverter &converter) {
77b3eb0e11SValentin Clement   llvm::StringRef name = intrinsic.name;
78b3eb0e11SValentin Clement   Fortran::evaluate::FoldingContext &fldCtx = converter.getFoldingContext();
79b3eb0e11SValentin Clement   return isMinOrMaxWithDynamicallyOptionalArg(name, procRef, fldCtx) ||
80b3eb0e11SValentin Clement          isIshftcWithDynamicallyOptionalArg(name, procRef, fldCtx) ||
8170ade047Svdonaldson          isRandomSeedWithDynamicallyOptionalArg(name, procRef, fldCtx);
82b3eb0e11SValentin Clement }
83b3eb0e11SValentin Clement 
prepareMinOrMaxArguments(const Fortran::evaluate::ProcedureRef & procRef,const Fortran::evaluate::SpecificIntrinsic & intrinsic,llvm::Optional<mlir::Type> retTy,const Fortran::lower::OperandPrepare & prepareOptionalArgument,const Fortran::lower::OperandPrepare & prepareOtherArgument,Fortran::lower::AbstractConverter & converter)84b3eb0e11SValentin Clement static void prepareMinOrMaxArguments(
85b3eb0e11SValentin Clement     const Fortran::evaluate::ProcedureRef &procRef,
86b3eb0e11SValentin Clement     const Fortran::evaluate::SpecificIntrinsic &intrinsic,
87b3eb0e11SValentin Clement     llvm::Optional<mlir::Type> retTy,
88b3eb0e11SValentin Clement     const Fortran::lower::OperandPrepare &prepareOptionalArgument,
89b3eb0e11SValentin Clement     const Fortran::lower::OperandPrepare &prepareOtherArgument,
90b3eb0e11SValentin Clement     Fortran::lower::AbstractConverter &converter) {
91b3eb0e11SValentin Clement   assert(retTy && "MIN and MAX must have a return type");
92ed8fceaaSKazu Hirata   mlir::Type resultType = *retTy;
93b3eb0e11SValentin Clement   mlir::Location loc = converter.getCurrentLocation();
94b3eb0e11SValentin Clement   if (fir::isa_char(resultType))
9539377d52SValentin Clement     TODO(loc, "CHARACTER MIN and MAX with dynamically optional arguments");
96b3eb0e11SValentin Clement   for (auto arg : llvm::enumerate(procRef.arguments())) {
97b3eb0e11SValentin Clement     const auto *expr =
98b3eb0e11SValentin Clement         Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg.value());
99b3eb0e11SValentin Clement     if (!expr)
100b3eb0e11SValentin Clement       continue;
101b3eb0e11SValentin Clement     if (arg.index() <= 1 || !Fortran::evaluate::MayBePassedAsAbsentOptional(
102b3eb0e11SValentin Clement                                 *expr, converter.getFoldingContext())) {
103b3eb0e11SValentin Clement       // Non optional arguments.
104b3eb0e11SValentin Clement       prepareOtherArgument(*expr);
105b3eb0e11SValentin Clement     } else {
106b3eb0e11SValentin Clement       // Dynamically optional arguments.
107b3eb0e11SValentin Clement       // Subtle: even for scalar the if-then-else will be generated in the loop
108b3eb0e11SValentin Clement       // nest because the then part will require the current extremum value that
109b3eb0e11SValentin Clement       // may depend on previous array element argument and cannot be outlined.
110b3eb0e11SValentin Clement       prepareOptionalArgument(*expr);
111b3eb0e11SValentin Clement     }
112b3eb0e11SValentin Clement   }
113b3eb0e11SValentin Clement }
114b3eb0e11SValentin Clement 
115b3eb0e11SValentin Clement static fir::ExtendedValue
lowerMinOrMax(fir::FirOpBuilder & builder,mlir::Location loc,llvm::StringRef name,llvm::Optional<mlir::Type> retTy,const Fortran::lower::OperandPresent & isPresentCheck,const Fortran::lower::OperandGetter & getOperand,std::size_t numOperands,Fortran::lower::StatementContext & stmtCtx)116b3eb0e11SValentin Clement lowerMinOrMax(fir::FirOpBuilder &builder, mlir::Location loc,
117b3eb0e11SValentin Clement               llvm::StringRef name, llvm::Optional<mlir::Type> retTy,
118b3eb0e11SValentin Clement               const Fortran::lower::OperandPresent &isPresentCheck,
119b3eb0e11SValentin Clement               const Fortran::lower::OperandGetter &getOperand,
120b3eb0e11SValentin Clement               std::size_t numOperands,
121b3eb0e11SValentin Clement               Fortran::lower::StatementContext &stmtCtx) {
122b3eb0e11SValentin Clement   assert(numOperands >= 2 && !isPresentCheck(0) && !isPresentCheck(1) &&
123b3eb0e11SValentin Clement          "min/max must have at least two non-optional args");
124b3eb0e11SValentin Clement   assert(retTy && "MIN and MAX must have a return type");
125ed8fceaaSKazu Hirata   mlir::Type resultType = *retTy;
126b3eb0e11SValentin Clement   llvm::SmallVector<fir::ExtendedValue> args;
127b3eb0e11SValentin Clement   args.push_back(getOperand(0));
128b3eb0e11SValentin Clement   args.push_back(getOperand(1));
129b3eb0e11SValentin Clement   mlir::Value extremum = fir::getBase(Fortran::lower::genIntrinsicCall(
130b3eb0e11SValentin Clement       builder, loc, name, resultType, args, stmtCtx));
131b3eb0e11SValentin Clement 
132b3eb0e11SValentin Clement   for (std::size_t opIndex = 2; opIndex < numOperands; ++opIndex) {
133b3eb0e11SValentin Clement     if (llvm::Optional<mlir::Value> isPresentRuntimeCheck =
134b3eb0e11SValentin Clement             isPresentCheck(opIndex)) {
135b3eb0e11SValentin Clement       // Argument is dynamically optional.
136b3eb0e11SValentin Clement       extremum =
137b3eb0e11SValentin Clement           builder
138ed8fceaaSKazu Hirata               .genIfOp(loc, {resultType}, *isPresentRuntimeCheck,
139b3eb0e11SValentin Clement                        /*withElseRegion=*/true)
140b3eb0e11SValentin Clement               .genThen([&]() {
141b3eb0e11SValentin Clement                 llvm::SmallVector<fir::ExtendedValue> args;
142b3eb0e11SValentin Clement                 args.emplace_back(extremum);
143b3eb0e11SValentin Clement                 args.emplace_back(getOperand(opIndex));
144b3eb0e11SValentin Clement                 fir::ExtendedValue newExtremum =
145b3eb0e11SValentin Clement                     Fortran::lower::genIntrinsicCall(builder, loc, name,
146b3eb0e11SValentin Clement                                                      resultType, args, stmtCtx);
147b3eb0e11SValentin Clement                 builder.create<fir::ResultOp>(loc, fir::getBase(newExtremum));
148b3eb0e11SValentin Clement               })
149b3eb0e11SValentin Clement               .genElse([&]() { builder.create<fir::ResultOp>(loc, extremum); })
150b3eb0e11SValentin Clement               .getResults()[0];
151b3eb0e11SValentin Clement     } else {
152b3eb0e11SValentin Clement       // Argument is know to be present at compile time.
153b3eb0e11SValentin Clement       llvm::SmallVector<fir::ExtendedValue> args;
154b3eb0e11SValentin Clement       args.emplace_back(extremum);
155b3eb0e11SValentin Clement       args.emplace_back(getOperand(opIndex));
156b3eb0e11SValentin Clement       extremum = fir::getBase(Fortran::lower::genIntrinsicCall(
157b3eb0e11SValentin Clement           builder, loc, name, resultType, args, stmtCtx));
158b3eb0e11SValentin Clement     }
159b3eb0e11SValentin Clement   }
160b3eb0e11SValentin Clement   return extremum;
161b3eb0e11SValentin Clement }
162b3eb0e11SValentin Clement 
prepareIshftcArguments(const Fortran::evaluate::ProcedureRef & procRef,const Fortran::evaluate::SpecificIntrinsic & intrinsic,llvm::Optional<mlir::Type> retTy,const Fortran::lower::OperandPrepare & prepareOptionalArgument,const Fortran::lower::OperandPrepare & prepareOtherArgument,Fortran::lower::AbstractConverter & converter)163b3eb0e11SValentin Clement static void prepareIshftcArguments(
164b3eb0e11SValentin Clement     const Fortran::evaluate::ProcedureRef &procRef,
165b3eb0e11SValentin Clement     const Fortran::evaluate::SpecificIntrinsic &intrinsic,
166b3eb0e11SValentin Clement     llvm::Optional<mlir::Type> retTy,
167b3eb0e11SValentin Clement     const Fortran::lower::OperandPrepare &prepareOptionalArgument,
168b3eb0e11SValentin Clement     const Fortran::lower::OperandPrepare &prepareOtherArgument,
169b3eb0e11SValentin Clement     Fortran::lower::AbstractConverter &converter) {
170b3eb0e11SValentin Clement   for (auto arg : llvm::enumerate(procRef.arguments())) {
171b3eb0e11SValentin Clement     const auto *expr =
172b3eb0e11SValentin Clement         Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg.value());
173b3eb0e11SValentin Clement     assert(expr && "expected all ISHFTC argument to be textually present here");
174b3eb0e11SValentin Clement     if (arg.index() == 2) {
175b3eb0e11SValentin Clement       assert(Fortran::evaluate::MayBePassedAsAbsentOptional(
176b3eb0e11SValentin Clement                  *expr, converter.getFoldingContext()) &&
177b3eb0e11SValentin Clement              "expected ISHFTC SIZE arg to be dynamically optional");
178b3eb0e11SValentin Clement       prepareOptionalArgument(*expr);
179b3eb0e11SValentin Clement     } else {
180b3eb0e11SValentin Clement       // Non optional arguments.
181b3eb0e11SValentin Clement       prepareOtherArgument(*expr);
182b3eb0e11SValentin Clement     }
183b3eb0e11SValentin Clement   }
184b3eb0e11SValentin Clement }
185b3eb0e11SValentin Clement 
186b3eb0e11SValentin Clement static fir::ExtendedValue
lowerIshftc(fir::FirOpBuilder & builder,mlir::Location loc,llvm::StringRef name,llvm::Optional<mlir::Type> retTy,const Fortran::lower::OperandPresent & isPresentCheck,const Fortran::lower::OperandGetter & getOperand,std::size_t numOperands,Fortran::lower::StatementContext & stmtCtx)187b3eb0e11SValentin Clement lowerIshftc(fir::FirOpBuilder &builder, mlir::Location loc,
188b3eb0e11SValentin Clement             llvm::StringRef name, llvm::Optional<mlir::Type> retTy,
189b3eb0e11SValentin Clement             const Fortran::lower::OperandPresent &isPresentCheck,
190b3eb0e11SValentin Clement             const Fortran::lower::OperandGetter &getOperand,
191b3eb0e11SValentin Clement             std::size_t numOperands,
192b3eb0e11SValentin Clement             Fortran::lower::StatementContext &stmtCtx) {
193b3eb0e11SValentin Clement   assert(numOperands == 3 && !isPresentCheck(0) && !isPresentCheck(1) &&
194b3eb0e11SValentin Clement          isPresentCheck(2) &&
195b3eb0e11SValentin Clement          "only ISHFTC SIZE arg is expected to be dynamically optional here");
196b3eb0e11SValentin Clement   assert(retTy && "ISFHTC must have a return type");
197*993625beSKazu Hirata   mlir::Type resultType = retTy.value();
198b3eb0e11SValentin Clement   llvm::SmallVector<fir::ExtendedValue> args;
199b3eb0e11SValentin Clement   args.push_back(getOperand(0));
200b3eb0e11SValentin Clement   args.push_back(getOperand(1));
201b3eb0e11SValentin Clement   args.push_back(builder
202*993625beSKazu Hirata                      .genIfOp(loc, {resultType}, isPresentCheck(2).value(),
203b3eb0e11SValentin Clement                               /*withElseRegion=*/true)
204b3eb0e11SValentin Clement                      .genThen([&]() {
205b3eb0e11SValentin Clement                        fir::ExtendedValue sizeExv = getOperand(2);
206b3eb0e11SValentin Clement                        mlir::Value size = builder.createConvert(
207b3eb0e11SValentin Clement                            loc, resultType, fir::getBase(sizeExv));
208b3eb0e11SValentin Clement                        builder.create<fir::ResultOp>(loc, size);
209b3eb0e11SValentin Clement                      })
210b3eb0e11SValentin Clement                      .genElse([&]() {
211b3eb0e11SValentin Clement                        mlir::Value bitSize = builder.createIntegerConstant(
212b3eb0e11SValentin Clement                            loc, resultType,
213b3eb0e11SValentin Clement                            resultType.cast<mlir::IntegerType>().getWidth());
214b3eb0e11SValentin Clement                        builder.create<fir::ResultOp>(loc, bitSize);
215b3eb0e11SValentin Clement                      })
216b3eb0e11SValentin Clement                      .getResults()[0]);
217b3eb0e11SValentin Clement   return Fortran::lower::genIntrinsicCall(builder, loc, name, resultType, args,
218b3eb0e11SValentin Clement                                           stmtCtx);
219b3eb0e11SValentin Clement }
220b3eb0e11SValentin Clement 
prepareCustomIntrinsicArgument(const Fortran::evaluate::ProcedureRef & procRef,const Fortran::evaluate::SpecificIntrinsic & intrinsic,llvm::Optional<mlir::Type> retTy,const OperandPrepare & prepareOptionalArgument,const OperandPrepare & prepareOtherArgument,AbstractConverter & converter)221b3eb0e11SValentin Clement void Fortran::lower::prepareCustomIntrinsicArgument(
222b3eb0e11SValentin Clement     const Fortran::evaluate::ProcedureRef &procRef,
223b3eb0e11SValentin Clement     const Fortran::evaluate::SpecificIntrinsic &intrinsic,
224b3eb0e11SValentin Clement     llvm::Optional<mlir::Type> retTy,
225b3eb0e11SValentin Clement     const OperandPrepare &prepareOptionalArgument,
226b3eb0e11SValentin Clement     const OperandPrepare &prepareOtherArgument, AbstractConverter &converter) {
227b3eb0e11SValentin Clement   llvm::StringRef name = intrinsic.name;
228b3eb0e11SValentin Clement   if (name == "min" || name == "max")
229b3eb0e11SValentin Clement     return prepareMinOrMaxArguments(procRef, intrinsic, retTy,
230b3eb0e11SValentin Clement                                     prepareOptionalArgument,
231b3eb0e11SValentin Clement                                     prepareOtherArgument, converter);
232b3eb0e11SValentin Clement   if (name == "ishftc")
233b3eb0e11SValentin Clement     return prepareIshftcArguments(procRef, intrinsic, retTy,
234b3eb0e11SValentin Clement                                   prepareOptionalArgument, prepareOtherArgument,
235b3eb0e11SValentin Clement                                   converter);
236b3eb0e11SValentin Clement   TODO(converter.getCurrentLocation(),
237b3eb0e11SValentin Clement        "unhandled dynamically optional arguments in SYSTEM_CLOCK or "
238b3eb0e11SValentin Clement        "RANDOM_SEED");
239b3eb0e11SValentin Clement }
240b3eb0e11SValentin Clement 
lowerCustomIntrinsic(fir::FirOpBuilder & builder,mlir::Location loc,llvm::StringRef name,llvm::Optional<mlir::Type> retTy,const OperandPresent & isPresentCheck,const OperandGetter & getOperand,std::size_t numOperands,Fortran::lower::StatementContext & stmtCtx)241b3eb0e11SValentin Clement fir::ExtendedValue Fortran::lower::lowerCustomIntrinsic(
242b3eb0e11SValentin Clement     fir::FirOpBuilder &builder, mlir::Location loc, llvm::StringRef name,
243b3eb0e11SValentin Clement     llvm::Optional<mlir::Type> retTy, const OperandPresent &isPresentCheck,
244b3eb0e11SValentin Clement     const OperandGetter &getOperand, std::size_t numOperands,
245b3eb0e11SValentin Clement     Fortran::lower::StatementContext &stmtCtx) {
246b3eb0e11SValentin Clement   if (name == "min" || name == "max")
247b3eb0e11SValentin Clement     return lowerMinOrMax(builder, loc, name, retTy, isPresentCheck, getOperand,
248b3eb0e11SValentin Clement                          numOperands, stmtCtx);
249b3eb0e11SValentin Clement   if (name == "ishftc")
250b3eb0e11SValentin Clement     return lowerIshftc(builder, loc, name, retTy, isPresentCheck, getOperand,
251b3eb0e11SValentin Clement                        numOperands, stmtCtx);
252b3eb0e11SValentin Clement   TODO(loc, "unhandled dynamically optional arguments in SYSTEM_CLOCK or "
253b3eb0e11SValentin Clement             "RANDOM_SEED");
254b3eb0e11SValentin Clement }
255