1 #include "testing.h" 2 #include "../../lib/Evaluate/host.h" 3 #include "flang/Evaluate/call.h" 4 #include "flang/Evaluate/expression.h" 5 #include "flang/Evaluate/fold.h" 6 #include "flang/Evaluate/intrinsics-library.h" 7 #include "flang/Evaluate/intrinsics.h" 8 #include "flang/Evaluate/target.h" 9 #include "flang/Evaluate/tools.h" 10 #include <tuple> 11 12 using namespace Fortran::evaluate; 13 14 // helper to call functions on all types from tuple 15 template <typename... T> struct RunOnTypes {}; 16 template <typename Test, typename... T> 17 struct RunOnTypes<Test, std::tuple<T...>> { 18 static void Run() { (..., Test::template Run<T>()); } 19 }; 20 21 // test for fold.h GetScalarConstantValue function 22 struct TestGetScalarConstantValue { 23 template <typename T> static void Run() { 24 Expr<T> exprFullyTyped{Constant<T>{Scalar<T>{}}}; 25 Expr<SomeKind<T::category>> exprSomeKind{exprFullyTyped}; 26 Expr<SomeType> exprSomeType{exprSomeKind}; 27 TEST(GetScalarConstantValue<T>(exprFullyTyped).has_value()); 28 TEST(GetScalarConstantValue<T>(exprSomeKind).has_value()); 29 TEST(GetScalarConstantValue<T>(exprSomeType).has_value()); 30 } 31 }; 32 33 template <typename T> 34 Scalar<T> CallHostRt( 35 HostRuntimeWrapper func, FoldingContext &context, Scalar<T> x) { 36 return GetScalarConstantValue<T>( 37 func(context, {AsGenericExpr(Constant<T>{x})})) 38 .value(); 39 } 40 41 void TestHostRuntimeSubnormalFlushing() { 42 using R4 = Type<TypeCategory::Real, 4>; 43 if constexpr (std::is_same_v<host::HostType<R4>, float>) { 44 Fortran::parser::CharBlock src; 45 Fortran::parser::ContextualMessages messages{src, nullptr}; 46 Fortran::common::IntrinsicTypeDefaultKinds defaults; 47 auto intrinsics{Fortran::evaluate::IntrinsicProcTable::Configure(defaults)}; 48 TargetCharacteristics flushingTargetCharacteristics; 49 flushingTargetCharacteristics.set_areSubnormalsFlushedToZero(true); 50 TargetCharacteristics noFlushingTargetCharacteristics; 51 noFlushingTargetCharacteristics.set_areSubnormalsFlushedToZero(false); 52 FoldingContext flushingContext{ 53 messages, defaults, intrinsics, flushingTargetCharacteristics}; 54 FoldingContext noFlushingContext{ 55 messages, defaults, intrinsics, noFlushingTargetCharacteristics}; 56 57 DynamicType r4{R4{}.GetType()}; 58 // Test subnormal argument flushing 59 if (auto callable{GetHostRuntimeWrapper("log", r4, {r4})}) { 60 // Biggest IEEE 32bits subnormal power of two 61 const Scalar<R4> x1{Scalar<R4>::Word{0x00400000}}; 62 Scalar<R4> y1Flushing{CallHostRt<R4>(*callable, flushingContext, x1)}; 63 Scalar<R4> y1NoFlushing{CallHostRt<R4>(*callable, noFlushingContext, x1)}; 64 // We would expect y1Flushing to be NaN, but some libc logf implementation 65 // "workaround" subnormal flushing by returning a constant negative 66 // results for all subnormal values (-1.03972076416015625e2_4). In case of 67 // flushing, the result should still be different than -88 +/- 2%. 68 TEST(y1Flushing.IsInfinite() || 69 std::abs(host::CastFortranToHost<R4>(y1Flushing) + 88.) > 2); 70 TEST(!y1NoFlushing.IsInfinite() && 71 std::abs(host::CastFortranToHost<R4>(y1NoFlushing) + 88.) < 2); 72 } else { 73 TEST(false); 74 } 75 } else { 76 TEST(false); // Cannot run this test on the host 77 } 78 } 79 80 int main() { 81 RunOnTypes<TestGetScalarConstantValue, AllIntrinsicTypes>::Run(); 82 TestHostRuntimeSubnormalFlushing(); 83 return testing::Complete(); 84 } 85