xref: /llvm-project/flang/unittests/Evaluate/intrinsics.cpp (revision 46ade6d0ef8fea94fbc28c75bb4bed4d928fd64b)
1 #include "flang/Evaluate/intrinsics.h"
2 #include "testing.h"
3 #include "flang/Evaluate/common.h"
4 #include "flang/Evaluate/expression.h"
5 #include "flang/Evaluate/tools.h"
6 #include "flang/Parser/provenance.h"
7 #include "llvm/Support/raw_ostream.h"
8 #include <initializer_list>
9 #include <map>
10 #include <string>
11 
12 namespace Fortran::evaluate {
13 
14 class CookedStrings {
15 public:
16   CookedStrings() {}
17   explicit CookedStrings(const std::initializer_list<std::string> &ss) {
18     for (const auto &s : ss) {
19       Save(s);
20     }
21     Marshal();
22   }
23   void Save(const std::string &s) {
24     offsets_[s] = cooked_.Put(s);
25     cooked_.PutProvenance(allSources_.AddCompilerInsertion(s));
26   }
27   void Marshal() { cooked_.Marshal(allCookedSources_); }
28   parser::CharBlock operator()(const std::string &s) {
29     return {cooked_.AsCharBlock().begin() + offsets_[s], s.size()};
30   }
31   parser::ContextualMessages Messages(parser::Messages &buffer) {
32     return parser::ContextualMessages{cooked_.AsCharBlock(), &buffer};
33   }
34   void Emit(llvm::raw_ostream &o, const parser::Messages &messages) {
35     messages.Emit(o, allCookedSources_);
36   }
37 
38 private:
39   parser::AllSources allSources_;
40   parser::AllCookedSources allCookedSources_{allSources_};
41   parser::CookedSource &cooked_{allCookedSources_.NewCookedSource()};
42   std::map<std::string, std::size_t> offsets_;
43 };
44 
45 template <typename A> auto Const(A &&x) -> Constant<TypeOf<A>> {
46   return Constant<TypeOf<A>>{std::move(x)};
47 }
48 
49 template <typename A> struct NamedArg {
50   std::string keyword;
51   A value;
52 };
53 
54 template <typename A> static NamedArg<A> Named(std::string kw, A &&x) {
55   return {kw, std::move(x)};
56 }
57 
58 struct TestCall {
59   TestCall(const common::IntrinsicTypeDefaultKinds &d,
60       const IntrinsicProcTable &t, std::string n)
61       : defaults{d}, table{t}, name{n} {}
62   template <typename A> TestCall &Push(A &&x) {
63     args.emplace_back(AsGenericExpr(std::move(x)));
64     keywords.push_back("");
65     return *this;
66   }
67   template <typename A> TestCall &Push(NamedArg<A> &&x) {
68     args.emplace_back(AsGenericExpr(std::move(x.value)));
69     keywords.push_back(x.keyword);
70     strings.Save(x.keyword);
71     return *this;
72   }
73   template <typename A, typename... As> TestCall &Push(A &&x, As &&...xs) {
74     Push(std::move(x));
75     return Push(std::move(xs)...);
76   }
77   void Marshal() {
78     strings.Save(name);
79     strings.Marshal();
80     std::size_t j{0};
81     for (auto &kw : keywords) {
82       if (!kw.empty()) {
83         args[j]->set_keyword(strings(kw));
84       }
85       ++j;
86     }
87   }
88   void DoCall(std::optional<DynamicType> resultType = std::nullopt,
89       int rank = 0, bool isElemental = false) {
90     Marshal();
91     parser::CharBlock fName{strings(name)};
92     llvm::outs() << "function: " << fName.ToString();
93     char sep{'('};
94     for (const auto &a : args) {
95       llvm::outs() << sep;
96       sep = ',';
97       a->AsFortran(llvm::outs());
98     }
99     if (sep == '(') {
100       llvm::outs() << '(';
101     }
102     llvm::outs() << ')' << '\n';
103     llvm::outs().flush();
104     CallCharacteristics call{fName.ToString()};
105     auto messages{strings.Messages(buffer)};
106     FoldingContext context{messages, defaults, table};
107     std::optional<SpecificCall> si{table.Probe(call, args, context)};
108     if (resultType.has_value()) {
109       TEST(si.has_value());
110       TEST(messages.messages() && !messages.messages()->AnyFatalError());
111       if (si) {
112         const auto &proc{si->specificIntrinsic.characteristics.value()};
113         const auto &fr{proc.functionResult};
114         TEST(fr.has_value());
115         if (fr) {
116           const auto *ts{fr->GetTypeAndShape()};
117           TEST(ts != nullptr);
118           if (ts) {
119             TEST(*resultType == ts->type());
120             MATCH(rank, ts->Rank());
121           }
122         }
123         MATCH(isElemental,
124             proc.attrs.test(characteristics::Procedure::Attr::Elemental));
125       }
126     } else {
127       TEST(!si.has_value());
128       TEST((messages.messages() && messages.messages()->AnyFatalError()) ||
129           name == "bad");
130     }
131     strings.Emit(llvm::outs(), buffer);
132   }
133 
134   const common::IntrinsicTypeDefaultKinds &defaults;
135   const IntrinsicProcTable &table;
136   CookedStrings strings;
137   parser::Messages buffer;
138   ActualArguments args;
139   std::string name;
140   std::vector<std::string> keywords;
141 };
142 
143 void TestIntrinsics() {
144   common::IntrinsicTypeDefaultKinds defaults;
145   MATCH(4, defaults.GetDefaultKind(TypeCategory::Integer));
146   MATCH(4, defaults.GetDefaultKind(TypeCategory::Real));
147   IntrinsicProcTable table{IntrinsicProcTable::Configure(defaults)};
148   table.Dump(llvm::outs());
149 
150   using Int1 = Type<TypeCategory::Integer, 1>;
151   using Int4 = Type<TypeCategory::Integer, 4>;
152   using Int8 = Type<TypeCategory::Integer, 8>;
153   using Real4 = Type<TypeCategory::Real, 4>;
154   using Real8 = Type<TypeCategory::Real, 8>;
155   using Complex4 = Type<TypeCategory::Complex, 4>;
156   using Complex8 = Type<TypeCategory::Complex, 8>;
157   using Char = Type<TypeCategory::Character, 1>;
158   using Log4 = Type<TypeCategory::Logical, 4>;
159 
160   TestCall{defaults, table, "bad"}
161       .Push(Const(Scalar<Int4>{}))
162       .DoCall(); // bad intrinsic name
163   TestCall{defaults, table, "abs"}
164       .Push(Named("a", Const(Scalar<Int4>{})))
165       .DoCall(Int4::GetType());
166   TestCall{defaults, table, "abs"}
167       .Push(Const(Scalar<Int4>{}))
168       .DoCall(Int4::GetType());
169   TestCall{defaults, table, "abs"}
170       .Push(Named("bad", Const(Scalar<Int4>{})))
171       .DoCall(); // bad keyword
172   TestCall{defaults, table, "abs"}.DoCall(); // insufficient args
173   TestCall{defaults, table, "abs"}
174       .Push(Const(Scalar<Int4>{}))
175       .Push(Const(Scalar<Int4>{}))
176       .DoCall(); // too many args
177   TestCall{defaults, table, "abs"}
178       .Push(Const(Scalar<Int4>{}))
179       .Push(Named("a", Const(Scalar<Int4>{})))
180       .DoCall();
181   TestCall{defaults, table, "abs"}
182       .Push(Named("a", Const(Scalar<Int4>{})))
183       .Push(Const(Scalar<Int4>{}))
184       .DoCall();
185   TestCall{defaults, table, "abs"}
186       .Push(Const(Scalar<Int1>{}))
187       .DoCall(Int1::GetType());
188   TestCall{defaults, table, "abs"}
189       .Push(Const(Scalar<Int4>{}))
190       .DoCall(Int4::GetType());
191   TestCall{defaults, table, "abs"}
192       .Push(Const(Scalar<Int8>{}))
193       .DoCall(Int8::GetType());
194   TestCall{defaults, table, "abs"}
195       .Push(Const(Scalar<Real4>{}))
196       .DoCall(Real4::GetType());
197   TestCall{defaults, table, "abs"}
198       .Push(Const(Scalar<Real8>{}))
199       .DoCall(Real8::GetType());
200   TestCall{defaults, table, "abs"}
201       .Push(Const(Scalar<Complex4>{}))
202       .DoCall(Real4::GetType());
203   TestCall{defaults, table, "abs"}
204       .Push(Const(Scalar<Complex8>{}))
205       .DoCall(Real8::GetType());
206   TestCall{defaults, table, "abs"}.Push(Const(Scalar<Char>{})).DoCall();
207   TestCall{defaults, table, "abs"}.Push(Const(Scalar<Log4>{})).DoCall();
208 
209   // "Ext" in names for calls allowed as extensions
210   TestCall maxCallR{defaults, table, "max"}, maxCallI{defaults, table, "min"},
211       max0Call{defaults, table, "max0"}, max1Call{defaults, table, "max1"},
212       amin0Call{defaults, table, "amin0"}, amin1Call{defaults, table, "amin1"},
213       max0ExtCall{defaults, table, "max0"},
214       amin1ExtCall{defaults, table, "amin1"};
215   for (int j{0}; j < 10; ++j) {
216     maxCallR.Push(Const(Scalar<Real4>{}));
217     maxCallI.Push(Const(Scalar<Int4>{}));
218     max0Call.Push(Const(Scalar<Int4>{}));
219     max0ExtCall.Push(Const(Scalar<Real4>{}));
220     max1Call.Push(Const(Scalar<Real4>{}));
221     amin0Call.Push(Const(Scalar<Int4>{}));
222     amin1ExtCall.Push(Const(Scalar<Int4>{}));
223     amin1Call.Push(Const(Scalar<Real4>{}));
224   }
225   maxCallR.DoCall(Real4::GetType());
226   maxCallI.DoCall(Int4::GetType());
227   max0Call.DoCall(Int4::GetType());
228   max0ExtCall.DoCall(Int4::GetType());
229   max1Call.DoCall(Int4::GetType());
230   amin0Call.DoCall(Real4::GetType());
231   amin1Call.DoCall(Real4::GetType());
232   amin1ExtCall.DoCall(Real4::GetType());
233 
234   TestCall{defaults, table, "conjg"}
235       .Push(Const(Scalar<Complex4>{}))
236       .DoCall(Complex4::GetType());
237   TestCall{defaults, table, "conjg"}
238       .Push(Const(Scalar<Complex8>{}))
239       .DoCall(Complex8::GetType());
240   TestCall{defaults, table, "dconjg"}.Push(Const(Scalar<Complex4>{})).DoCall();
241   TestCall{defaults, table, "dconjg"}
242       .Push(Const(Scalar<Complex8>{}))
243       .DoCall(Complex8::GetType());
244 
245   TestCall{defaults, table, "float"}.Push(Const(Scalar<Real4>{})).DoCall();
246   TestCall{defaults, table, "float"}
247       .Push(Const(Scalar<Int4>{}))
248       .DoCall(Real4::GetType());
249   TestCall{defaults, table, "idint"}.Push(Const(Scalar<Int4>{})).DoCall();
250   TestCall{defaults, table, "idint"}
251       .Push(Const(Scalar<Real8>{}))
252       .DoCall(Int4::GetType());
253 
254   // Allowed as extensions
255   TestCall{defaults, table, "float"}
256       .Push(Const(Scalar<Int8>{}))
257       .DoCall(Real4::GetType());
258   TestCall{defaults, table, "idint"}
259       .Push(Const(Scalar<Real4>{}))
260       .DoCall(Int4::GetType());
261 
262   TestCall{defaults, table, "num_images"}.DoCall(Int4::GetType());
263   TestCall{defaults, table, "num_images"}
264       .Push(Const(Scalar<Int1>{}))
265       .DoCall(Int4::GetType());
266   TestCall{defaults, table, "num_images"}
267       .Push(Const(Scalar<Int4>{}))
268       .DoCall(Int4::GetType());
269   TestCall{defaults, table, "num_images"}
270       .Push(Const(Scalar<Int8>{}))
271       .DoCall(Int4::GetType());
272   TestCall{defaults, table, "num_images"}
273       .Push(Named("team_number", Const(Scalar<Int4>{})))
274       .DoCall(Int4::GetType());
275   TestCall{defaults, table, "num_images"}
276       .Push(Const(Scalar<Int4>{}))
277       .Push(Const(Scalar<Int4>{}))
278       .DoCall(); // too many args
279   TestCall{defaults, table, "num_images"}
280       .Push(Named("bad", Const(Scalar<Int4>{})))
281       .DoCall(); // bad keyword
282   TestCall{defaults, table, "num_images"}
283       .Push(Const(Scalar<Char>{}))
284       .DoCall(); // bad type
285   TestCall{defaults, table, "num_images"}
286       .Push(Const(Scalar<Log4>{}))
287       .DoCall(); // bad type
288   TestCall{defaults, table, "num_images"}
289       .Push(Const(Scalar<Complex8>{}))
290       .DoCall(); // bad type
291   TestCall{defaults, table, "num_images"}
292       .Push(Const(Scalar<Real4>{}))
293       .DoCall(); // bad type
294 
295   // TODO: test other intrinsics
296 
297   // Test unrestricted specific to generic name mapping (table 16.2).
298   TEST(table.GetGenericIntrinsicName("alog") == "log");
299   TEST(table.GetGenericIntrinsicName("alog10") == "log10");
300   TEST(table.GetGenericIntrinsicName("amod") == "mod");
301   TEST(table.GetGenericIntrinsicName("cabs") == "abs");
302   TEST(table.GetGenericIntrinsicName("ccos") == "cos");
303   TEST(table.GetGenericIntrinsicName("cexp") == "exp");
304   TEST(table.GetGenericIntrinsicName("clog") == "log");
305   TEST(table.GetGenericIntrinsicName("csin") == "sin");
306   TEST(table.GetGenericIntrinsicName("csqrt") == "sqrt");
307   TEST(table.GetGenericIntrinsicName("dabs") == "abs");
308   TEST(table.GetGenericIntrinsicName("dacos") == "acos");
309   TEST(table.GetGenericIntrinsicName("dasin") == "asin");
310   TEST(table.GetGenericIntrinsicName("datan") == "atan");
311   TEST(table.GetGenericIntrinsicName("datan2") == "atan2");
312   TEST(table.GetGenericIntrinsicName("dcos") == "cos");
313   TEST(table.GetGenericIntrinsicName("dcosh") == "cosh");
314   TEST(table.GetGenericIntrinsicName("ddim") == "dim");
315   TEST(table.GetGenericIntrinsicName("dexp") == "exp");
316   TEST(table.GetGenericIntrinsicName("dint") == "aint");
317   TEST(table.GetGenericIntrinsicName("dlog") == "log");
318   TEST(table.GetGenericIntrinsicName("dlog10") == "log10");
319   TEST(table.GetGenericIntrinsicName("dmod") == "mod");
320   TEST(table.GetGenericIntrinsicName("dnint") == "anint");
321   TEST(table.GetGenericIntrinsicName("dsign") == "sign");
322   TEST(table.GetGenericIntrinsicName("dsin") == "sin");
323   TEST(table.GetGenericIntrinsicName("dsinh") == "sinh");
324   TEST(table.GetGenericIntrinsicName("dsqrt") == "sqrt");
325   TEST(table.GetGenericIntrinsicName("dtan") == "tan");
326   TEST(table.GetGenericIntrinsicName("dtanh") == "tanh");
327   TEST(table.GetGenericIntrinsicName("iabs") == "abs");
328   TEST(table.GetGenericIntrinsicName("idim") == "dim");
329   TEST(table.GetGenericIntrinsicName("idnint") == "nint");
330   TEST(table.GetGenericIntrinsicName("isign") == "sign");
331   // Test a case where specific and generic name are the same.
332   TEST(table.GetGenericIntrinsicName("acos") == "acos");
333 }
334 } // namespace Fortran::evaluate
335 
336 int main() {
337   Fortran::evaluate::TestIntrinsics();
338   return testing::Complete();
339 }
340