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