mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 07:26:40 +00:00

The non-GTest library will be shared by unittests of Flang and Flang-RT. Promote it as a regular library for use by both projects. In the long term, we may want to convert these to regular GTest checks to avoid having multiple testing frameworks.
373 lines
13 KiB
C++
373 lines
13 KiB
C++
#include "flang/Evaluate/intrinsics.h"
|
|
#include "flang/Evaluate/common.h"
|
|
#include "flang/Evaluate/expression.h"
|
|
#include "flang/Evaluate/target.h"
|
|
#include "flang/Evaluate/tools.h"
|
|
#include "flang/Parser/provenance.h"
|
|
#include "flang/Testing/testing.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <initializer_list>
|
|
#include <map>
|
|
#include <string>
|
|
|
|
namespace Fortran::evaluate {
|
|
|
|
class CookedStrings {
|
|
public:
|
|
CookedStrings() {}
|
|
explicit CookedStrings(const std::initializer_list<std::string> &ss) {
|
|
for (const auto &s : ss) {
|
|
Save(s);
|
|
}
|
|
Marshal();
|
|
}
|
|
void Save(const std::string &s) {
|
|
offsets_[s] = cooked_.Put(s);
|
|
cooked_.PutProvenance(allSources_.AddCompilerInsertion(s));
|
|
}
|
|
void Marshal() { cooked_.Marshal(allCookedSources_); }
|
|
parser::CharBlock operator()(const std::string &s) {
|
|
return {cooked_.AsCharBlock().begin() + offsets_[s], s.size()};
|
|
}
|
|
parser::ContextualMessages Messages(parser::Messages &buffer) {
|
|
return parser::ContextualMessages{cooked_.AsCharBlock(), &buffer};
|
|
}
|
|
void Emit(llvm::raw_ostream &o, const parser::Messages &messages) {
|
|
messages.Emit(o, allCookedSources_);
|
|
}
|
|
|
|
private:
|
|
parser::AllSources allSources_;
|
|
parser::AllCookedSources allCookedSources_{allSources_};
|
|
parser::CookedSource &cooked_{allCookedSources_.NewCookedSource()};
|
|
std::map<std::string, std::size_t> offsets_;
|
|
};
|
|
|
|
template <typename A> auto Const(A &&x) -> Constant<TypeOf<A>> {
|
|
return Constant<TypeOf<A>>{std::move(x)};
|
|
}
|
|
|
|
template <typename A> struct NamedArg {
|
|
std::string keyword;
|
|
A value;
|
|
};
|
|
|
|
template <typename A> static NamedArg<A> Named(std::string kw, A &&x) {
|
|
return {kw, std::move(x)};
|
|
}
|
|
|
|
struct TestCall {
|
|
TestCall(const common::IntrinsicTypeDefaultKinds &d,
|
|
const IntrinsicProcTable &t, std::string n)
|
|
: defaults{d}, table{t}, name{n} {}
|
|
template <typename A> TestCall &Push(A &&x) {
|
|
args.emplace_back(AsGenericExpr(std::move(x)));
|
|
keywords.push_back("");
|
|
return *this;
|
|
}
|
|
template <typename A> TestCall &Push(NamedArg<A> &&x) {
|
|
args.emplace_back(AsGenericExpr(std::move(x.value)));
|
|
keywords.push_back(x.keyword);
|
|
strings.Save(x.keyword);
|
|
return *this;
|
|
}
|
|
template <typename A, typename... As> TestCall &Push(A &&x, As &&...xs) {
|
|
Push(std::move(x));
|
|
return Push(std::move(xs)...);
|
|
}
|
|
void Marshal() {
|
|
strings.Save(name);
|
|
strings.Marshal();
|
|
std::size_t j{0};
|
|
for (auto &kw : keywords) {
|
|
if (!kw.empty()) {
|
|
args[j]->set_keyword(strings(kw));
|
|
}
|
|
++j;
|
|
}
|
|
}
|
|
void DoCall(std::optional<DynamicType> resultType = std::nullopt,
|
|
int rank = 0, bool isElemental = false) {
|
|
Marshal();
|
|
parser::CharBlock fName{strings(name)};
|
|
llvm::outs() << "function: " << fName.ToString();
|
|
char sep{'('};
|
|
for (const auto &a : args) {
|
|
llvm::outs() << sep;
|
|
sep = ',';
|
|
a->AsFortran(llvm::outs());
|
|
}
|
|
if (sep == '(') {
|
|
llvm::outs() << '(';
|
|
}
|
|
llvm::outs() << ')' << '\n';
|
|
llvm::outs().flush();
|
|
CallCharacteristics call{fName.ToString()};
|
|
auto messages{strings.Messages(buffer)};
|
|
TargetCharacteristics targetCharacteristics;
|
|
common::LanguageFeatureControl languageFeatures;
|
|
FoldingContext context{messages, defaults, table, targetCharacteristics,
|
|
languageFeatures, tempNames};
|
|
std::optional<SpecificCall> si{table.Probe(call, args, context)};
|
|
if (resultType.has_value()) {
|
|
TEST(si.has_value());
|
|
TEST(messages.messages() && !messages.messages()->AnyFatalError());
|
|
if (si) {
|
|
const auto &proc{si->specificIntrinsic.characteristics.value()};
|
|
const auto &fr{proc.functionResult};
|
|
TEST(fr.has_value());
|
|
if (fr) {
|
|
const auto *ts{fr->GetTypeAndShape()};
|
|
TEST(ts != nullptr);
|
|
if (ts) {
|
|
TEST(*resultType == ts->type());
|
|
MATCH(rank, ts->Rank());
|
|
}
|
|
}
|
|
MATCH(isElemental,
|
|
proc.attrs.test(characteristics::Procedure::Attr::Elemental));
|
|
}
|
|
} else {
|
|
TEST(!si.has_value());
|
|
TEST((messages.messages() && messages.messages()->AnyFatalError()) ||
|
|
name == "bad");
|
|
}
|
|
strings.Emit(llvm::outs(), buffer);
|
|
}
|
|
|
|
const common::IntrinsicTypeDefaultKinds &defaults;
|
|
const IntrinsicProcTable &table;
|
|
CookedStrings strings;
|
|
parser::Messages buffer;
|
|
ActualArguments args;
|
|
std::string name;
|
|
std::vector<std::string> keywords;
|
|
std::set<std::string> tempNames;
|
|
};
|
|
|
|
void TestIntrinsics() {
|
|
common::IntrinsicTypeDefaultKinds defaults;
|
|
MATCH(4, defaults.GetDefaultKind(TypeCategory::Integer));
|
|
MATCH(4, defaults.GetDefaultKind(TypeCategory::Real));
|
|
IntrinsicProcTable table{IntrinsicProcTable::Configure(defaults)};
|
|
table.Dump(llvm::outs());
|
|
|
|
using Int1 = Type<TypeCategory::Integer, 1>;
|
|
using Int4 = Type<TypeCategory::Integer, 4>;
|
|
using Int8 = Type<TypeCategory::Integer, 8>;
|
|
using Real4 = Type<TypeCategory::Real, 4>;
|
|
using Real8 = Type<TypeCategory::Real, 8>;
|
|
using Complex4 = Type<TypeCategory::Complex, 4>;
|
|
using Complex8 = Type<TypeCategory::Complex, 8>;
|
|
using Char = Type<TypeCategory::Character, 1>;
|
|
using Log4 = Type<TypeCategory::Logical, 4>;
|
|
|
|
TestCall{defaults, table, "bad"}
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.DoCall(); // bad intrinsic name
|
|
TestCall{defaults, table, "abs"}
|
|
.Push(Named("a", Const(Scalar<Int4>{})))
|
|
.DoCall(Int4::GetType());
|
|
TestCall{defaults, table, "abs"}
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.DoCall(Int4::GetType());
|
|
TestCall{defaults, table, "abs"}
|
|
.Push(Named("bad", Const(Scalar<Int4>{})))
|
|
.DoCall(); // bad keyword
|
|
TestCall{defaults, table, "abs"}.DoCall(); // insufficient args
|
|
TestCall{defaults, table, "abs"}
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.DoCall(); // too many args
|
|
TestCall{defaults, table, "abs"}
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.Push(Named("a", Const(Scalar<Int4>{})))
|
|
.DoCall();
|
|
TestCall{defaults, table, "abs"}
|
|
.Push(Named("a", Const(Scalar<Int4>{})))
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.DoCall();
|
|
TestCall{defaults, table, "abs"}
|
|
.Push(Const(Scalar<Int1>{}))
|
|
.DoCall(Int1::GetType());
|
|
TestCall{defaults, table, "abs"}
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.DoCall(Int4::GetType());
|
|
TestCall{defaults, table, "abs"}
|
|
.Push(Const(Scalar<Int8>{}))
|
|
.DoCall(Int8::GetType());
|
|
TestCall{defaults, table, "abs"}
|
|
.Push(Const(Scalar<Real4>{}))
|
|
.DoCall(Real4::GetType());
|
|
TestCall{defaults, table, "abs"}
|
|
.Push(Const(Scalar<Real8>{}))
|
|
.DoCall(Real8::GetType());
|
|
TestCall{defaults, table, "abs"}
|
|
.Push(Const(Scalar<Complex4>{}))
|
|
.DoCall(Real4::GetType());
|
|
TestCall{defaults, table, "abs"}
|
|
.Push(Const(Scalar<Complex8>{}))
|
|
.DoCall(Real8::GetType());
|
|
TestCall{defaults, table, "abs"}.Push(Const(Scalar<Char>{})).DoCall();
|
|
TestCall{defaults, table, "abs"}.Push(Const(Scalar<Log4>{})).DoCall();
|
|
|
|
// "Ext" in names for calls allowed as extensions
|
|
TestCall maxCallR{defaults, table, "max"}, maxCallI{defaults, table, "min"},
|
|
max0Call{defaults, table, "max0"}, max1Call{defaults, table, "max1"},
|
|
amin0Call{defaults, table, "amin0"}, amin1Call{defaults, table, "amin1"},
|
|
max0ExtCall{defaults, table, "max0"},
|
|
amin1ExtCall{defaults, table, "amin1"};
|
|
for (int j{0}; j < 10; ++j) {
|
|
maxCallR.Push(Const(Scalar<Real4>{}));
|
|
maxCallI.Push(Const(Scalar<Int4>{}));
|
|
max0Call.Push(Const(Scalar<Int4>{}));
|
|
max0ExtCall.Push(Const(Scalar<Real4>{}));
|
|
max1Call.Push(Const(Scalar<Real4>{}));
|
|
amin0Call.Push(Const(Scalar<Int4>{}));
|
|
amin1ExtCall.Push(Const(Scalar<Int4>{}));
|
|
amin1Call.Push(Const(Scalar<Real4>{}));
|
|
}
|
|
maxCallR.DoCall(Real4::GetType());
|
|
maxCallI.DoCall(Int4::GetType());
|
|
max0Call.DoCall(Int4::GetType());
|
|
max0ExtCall.DoCall(Int4::GetType());
|
|
max1Call.DoCall(Int4::GetType());
|
|
amin0Call.DoCall(Real4::GetType());
|
|
amin1Call.DoCall(Real4::GetType());
|
|
amin1ExtCall.DoCall(Real4::GetType());
|
|
|
|
TestCall{defaults, table, "conjg"}
|
|
.Push(Const(Scalar<Complex4>{}))
|
|
.DoCall(Complex4::GetType());
|
|
TestCall{defaults, table, "conjg"}
|
|
.Push(Const(Scalar<Complex8>{}))
|
|
.DoCall(Complex8::GetType());
|
|
TestCall{defaults, table, "dconjg"}
|
|
.Push(Const(Scalar<Complex8>{}))
|
|
.DoCall(Complex8::GetType());
|
|
|
|
TestCall{defaults, table, "float"}.Push(Const(Scalar<Real4>{})).DoCall();
|
|
TestCall{defaults, table, "float"}
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.DoCall(Real4::GetType());
|
|
TestCall{defaults, table, "idint"}.Push(Const(Scalar<Int4>{})).DoCall();
|
|
TestCall{defaults, table, "idint"}
|
|
.Push(Const(Scalar<Real8>{}))
|
|
.DoCall(Int4::GetType());
|
|
|
|
// Allowed as extensions
|
|
TestCall{defaults, table, "float"}
|
|
.Push(Const(Scalar<Int8>{}))
|
|
.DoCall(Real4::GetType());
|
|
TestCall{defaults, table, "idint"}
|
|
.Push(Const(Scalar<Real4>{}))
|
|
.DoCall(Int4::GetType());
|
|
|
|
TestCall{defaults, table, "num_images"}.DoCall(Int4::GetType());
|
|
TestCall{defaults, table, "num_images"}
|
|
.Push(Const(Scalar<Int1>{}))
|
|
.DoCall(Int4::GetType());
|
|
TestCall{defaults, table, "num_images"}
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.DoCall(Int4::GetType());
|
|
TestCall{defaults, table, "num_images"}
|
|
.Push(Const(Scalar<Int8>{}))
|
|
.DoCall(Int4::GetType());
|
|
TestCall{defaults, table, "num_images"}
|
|
.Push(Named("team_number", Const(Scalar<Int4>{})))
|
|
.DoCall(Int4::GetType());
|
|
TestCall{defaults, table, "num_images"}
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.DoCall(); // too many args
|
|
TestCall{defaults, table, "num_images"}
|
|
.Push(Named("bad", Const(Scalar<Int4>{})))
|
|
.DoCall(); // bad keyword
|
|
TestCall{defaults, table, "num_images"}
|
|
.Push(Const(Scalar<Char>{}))
|
|
.DoCall(); // bad type
|
|
TestCall{defaults, table, "num_images"}
|
|
.Push(Const(Scalar<Log4>{}))
|
|
.DoCall(); // bad type
|
|
TestCall{defaults, table, "num_images"}
|
|
.Push(Const(Scalar<Complex8>{}))
|
|
.DoCall(); // bad type
|
|
TestCall{defaults, table, "num_images"}
|
|
.Push(Const(Scalar<Real4>{}))
|
|
.DoCall(); // bad type
|
|
|
|
// This test temporarily removed because it requires access to
|
|
// the ISO_FORTRAN_ENV intrinsic module. This module should to
|
|
// be loaded (somehow) and the following test reinstated.
|
|
// TestCall{defaults, table, "team_number"}.DoCall(Int4::GetType());
|
|
|
|
TestCall{defaults, table, "team_number"}
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.DoCall(); // too many args
|
|
TestCall{defaults, table, "team_number"}
|
|
.Push(Named("bad", Const(Scalar<Int4>{})))
|
|
.DoCall(); // bad keyword
|
|
TestCall{defaults, table, "team_number"}
|
|
.Push(Const(Scalar<Int4>{}))
|
|
.DoCall(); // bad type
|
|
TestCall{defaults, table, "team_number"}
|
|
.Push(Const(Scalar<Char>{}))
|
|
.DoCall(); // bad type
|
|
TestCall{defaults, table, "team_number"}
|
|
.Push(Const(Scalar<Log4>{}))
|
|
.DoCall(); // bad type
|
|
TestCall{defaults, table, "team_number"}
|
|
.Push(Const(Scalar<Complex8>{}))
|
|
.DoCall(); // bad type
|
|
TestCall{defaults, table, "team_number"}
|
|
.Push(Const(Scalar<Real4>{}))
|
|
.DoCall(); // bad type
|
|
|
|
// TODO: test other intrinsics
|
|
|
|
// Test unrestricted specific to generic name mapping (table 16.2).
|
|
TEST(table.GetGenericIntrinsicName("alog") == "log");
|
|
TEST(table.GetGenericIntrinsicName("alog10") == "log10");
|
|
TEST(table.GetGenericIntrinsicName("amod") == "mod");
|
|
TEST(table.GetGenericIntrinsicName("cabs") == "abs");
|
|
TEST(table.GetGenericIntrinsicName("ccos") == "cos");
|
|
TEST(table.GetGenericIntrinsicName("cexp") == "exp");
|
|
TEST(table.GetGenericIntrinsicName("clog") == "log");
|
|
TEST(table.GetGenericIntrinsicName("csin") == "sin");
|
|
TEST(table.GetGenericIntrinsicName("csqrt") == "sqrt");
|
|
TEST(table.GetGenericIntrinsicName("dabs") == "abs");
|
|
TEST(table.GetGenericIntrinsicName("dacos") == "acos");
|
|
TEST(table.GetGenericIntrinsicName("dasin") == "asin");
|
|
TEST(table.GetGenericIntrinsicName("datan") == "atan");
|
|
TEST(table.GetGenericIntrinsicName("datan2") == "atan2");
|
|
TEST(table.GetGenericIntrinsicName("dcos") == "cos");
|
|
TEST(table.GetGenericIntrinsicName("dcosh") == "cosh");
|
|
TEST(table.GetGenericIntrinsicName("ddim") == "dim");
|
|
TEST(table.GetGenericIntrinsicName("derf") == "erf");
|
|
TEST(table.GetGenericIntrinsicName("dexp") == "exp");
|
|
TEST(table.GetGenericIntrinsicName("dint") == "aint");
|
|
TEST(table.GetGenericIntrinsicName("dlog") == "log");
|
|
TEST(table.GetGenericIntrinsicName("dlog10") == "log10");
|
|
TEST(table.GetGenericIntrinsicName("dmod") == "mod");
|
|
TEST(table.GetGenericIntrinsicName("dnint") == "anint");
|
|
TEST(table.GetGenericIntrinsicName("dsign") == "sign");
|
|
TEST(table.GetGenericIntrinsicName("dsin") == "sin");
|
|
TEST(table.GetGenericIntrinsicName("dsinh") == "sinh");
|
|
TEST(table.GetGenericIntrinsicName("dsqrt") == "sqrt");
|
|
TEST(table.GetGenericIntrinsicName("dtan") == "tan");
|
|
TEST(table.GetGenericIntrinsicName("dtanh") == "tanh");
|
|
TEST(table.GetGenericIntrinsicName("iabs") == "abs");
|
|
TEST(table.GetGenericIntrinsicName("idim") == "dim");
|
|
TEST(table.GetGenericIntrinsicName("idnint") == "nint");
|
|
TEST(table.GetGenericIntrinsicName("isign") == "sign");
|
|
// Test a case where specific and generic name are the same.
|
|
TEST(table.GetGenericIntrinsicName("acos") == "acos");
|
|
}
|
|
} // namespace Fortran::evaluate
|
|
|
|
int main() {
|
|
Fortran::evaluate::TestIntrinsics();
|
|
return testing::Complete();
|
|
}
|