CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
145 lines
4.3 KiB
C++
145 lines
4.3 KiB
C++
//===-- unittests/Runtime/Exceptions.cpp ------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
/// Tests for the Fortran runtime fenv-wrapping entry points
|
|
/// (feclearexcept / feraiseexcept / fetestexcept).
|
|
///
|
|
/// These checks also guard against an optimizer eliding the wrapped libm
|
|
/// calls: under clang's default `-ffp-exception-behavior=ignore`, the
|
|
/// compiler is free to drop calls to fenv functions, which would silently
|
|
/// break ieee_arithmetic flag handling. exceptions.cpp uses
|
|
/// `FLANG_FP_TRAP_ON` to disable that optimization; if it were ever removed
|
|
/// or weakened, the round-trip assertions below would fail.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "flang/Runtime/exceptions.h"
|
|
#include "flang/Common/fp-control.h"
|
|
#include <cfenv>
|
|
#include <cstdint>
|
|
#include <gtest/gtest.h>
|
|
|
|
using namespace Fortran::runtime;
|
|
|
|
namespace {
|
|
|
|
// Saves and restores the host floating-point environment so tests do not
|
|
// leak exception flags into each other or into the rest of the suite.
|
|
class FenvScope {
|
|
public:
|
|
FenvScope() {
|
|
FLANG_FP_TRAP_ON
|
|
std::fegetenv(&saved_);
|
|
}
|
|
~FenvScope() {
|
|
FLANG_FP_TRAP_ON
|
|
std::fesetenv(&saved_);
|
|
}
|
|
|
|
private:
|
|
std::fenv_t saved_;
|
|
};
|
|
|
|
// Bitmask of every standard exception this platform defines. C99 lets
|
|
// some be absent (e.g. on musl/emscripten); the runtime itself guards
|
|
// each one, so the tests mirror that.
|
|
constexpr std::uint32_t kAllSupported = 0u
|
|
#ifdef FE_INVALID
|
|
| FE_INVALID
|
|
#endif
|
|
#ifdef FE_DIVBYZERO
|
|
| FE_DIVBYZERO
|
|
#endif
|
|
#ifdef FE_OVERFLOW
|
|
| FE_OVERFLOW
|
|
#endif
|
|
#ifdef FE_UNDERFLOW
|
|
| FE_UNDERFLOW
|
|
#endif
|
|
#ifdef FE_INEXACT
|
|
| FE_INEXACT
|
|
#endif
|
|
;
|
|
|
|
} // namespace
|
|
|
|
TEST(Exceptions, ClearLeavesAllUnset) {
|
|
FenvScope guard;
|
|
if (kAllSupported == 0u) {
|
|
GTEST_SKIP() << "no FE_* exceptions defined on this platform";
|
|
}
|
|
RTNAME(feraiseexcept)(kAllSupported);
|
|
RTNAME(feclearexcept)(kAllSupported);
|
|
EXPECT_EQ(RTNAME(fetestexcept)(kAllSupported), 0u);
|
|
}
|
|
|
|
TEST(Exceptions, RaiseSetsExactlyRequestedFlag) {
|
|
FenvScope guard;
|
|
RTNAME(feclearexcept)(kAllSupported);
|
|
|
|
// Walk every supported exception bit individually. After raising one,
|
|
// fetestexcept(that bit) must report it set; fetestexcept(other bits)
|
|
// must not spuriously light up.
|
|
const std::uint32_t bits[] = {
|
|
#ifdef FE_INVALID
|
|
static_cast<std::uint32_t>(FE_INVALID),
|
|
#endif
|
|
#ifdef FE_DIVBYZERO
|
|
static_cast<std::uint32_t>(FE_DIVBYZERO),
|
|
#endif
|
|
#ifdef FE_OVERFLOW
|
|
static_cast<std::uint32_t>(FE_OVERFLOW),
|
|
#endif
|
|
#ifdef FE_UNDERFLOW
|
|
static_cast<std::uint32_t>(FE_UNDERFLOW),
|
|
#endif
|
|
#ifdef FE_INEXACT
|
|
static_cast<std::uint32_t>(FE_INEXACT),
|
|
#endif
|
|
};
|
|
if (sizeof(bits) == 0) {
|
|
GTEST_SKIP() << "no FE_* exceptions defined on this platform";
|
|
}
|
|
|
|
// C99 7.6.2.3 p2 leaves it implementation-defined whether feraiseexcept
|
|
// additionally raises FE_INEXACT when raising FE_OVERFLOW or FE_UNDERFLOW.
|
|
// AArch64 glibc does, so exclude FE_INEXACT from the "no spurious flag"
|
|
// mask to keep the test portable.
|
|
constexpr std::uint32_t kInexactTolerated =
|
|
#ifdef FE_INEXACT
|
|
static_cast<std::uint32_t>(FE_INEXACT);
|
|
#else
|
|
0u;
|
|
#endif
|
|
|
|
for (std::uint32_t bit : bits) {
|
|
RTNAME(feclearexcept)(kAllSupported);
|
|
RTNAME(feraiseexcept)(bit);
|
|
EXPECT_NE(RTNAME(fetestexcept)(bit), 0u) << "bit 0x" << std::hex << bit;
|
|
const std::uint32_t forbidden = kAllSupported & ~bit & ~kInexactTolerated;
|
|
EXPECT_EQ(RTNAME(fetestexcept)(forbidden), 0u)
|
|
<< "spurious flag set when raising 0x" << std::hex << bit;
|
|
}
|
|
}
|
|
|
|
TEST(Exceptions, ClearOneLeavesOthersAlone) {
|
|
FenvScope guard;
|
|
#if defined(FE_OVERFLOW) && defined(FE_INVALID)
|
|
RTNAME(feclearexcept)(kAllSupported);
|
|
RTNAME(feraiseexcept)(FE_OVERFLOW | FE_INVALID);
|
|
ASSERT_NE(RTNAME(fetestexcept)(FE_OVERFLOW), 0u);
|
|
ASSERT_NE(RTNAME(fetestexcept)(FE_INVALID), 0u);
|
|
|
|
RTNAME(feclearexcept)(FE_OVERFLOW);
|
|
EXPECT_EQ(RTNAME(fetestexcept)(FE_OVERFLOW), 0u);
|
|
EXPECT_NE(RTNAME(fetestexcept)(FE_INVALID), 0u);
|
|
#else
|
|
GTEST_SKIP() << "FE_OVERFLOW and FE_INVALID required for this test";
|
|
#endif
|
|
}
|