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