From d73f22407657709aba05a643facd43ff1060d109 Mon Sep 17 00:00:00 2001 From: Sam Yates <yates@cscs.ch> Date: Wed, 30 Nov 2016 09:20:54 +0100 Subject: [PATCH] Work around xlC 13.1.4 compiler bugs. (#115) Addresses in part issue #113. * Make compatibility wrappers/functions that dance around xlC bugs. Wrappers are provied in `util/compat.hpp` and live in the `compat` namespace. - `compat::end` reimplements `std::end` but in a way that apparently does not trigger the xlC bug. - `compat::compiler_barrier_if_xlc_leq()` inserts a compiler reordering barrier if the compiler is xlC and the version less than or equal to that specified. Name is deliberately verbose. - `compat::isinf()` is an inline wrapper around `std::isinf()`, which apparently is sufficient to defuse an evaluation order bug with `std::isinf()` in switch statements. * Use `compat::compiler_barrier_if_xlc_leq()` in `util::unitialized` reference access methods to avoid improper reordering with -O2. * Use `compat::isinf()` in `test_math.cpp` to defuse improper reordering within `EXPECT_EQ` gtest macro of `std::isinf()`. * Use `compat::end()` in `util::back()` and `util::cend()` to avoid incorrect `std::end()` behaviour with -O2. * Use `util::cend()` in `algorithms::sum()`, again to avoid incorrect `std::end()` behaviour with -O2. --- src/algorithms.hpp | 2 +- src/util/compat.hpp | 37 +++++++++++++++++++++++++++++++++++++ src/util/iterutil.hpp | 4 +++- src/util/meta.hpp | 7 +++++-- src/util/uninitialized.hpp | 15 +++++++++++++-- tests/unit/test_math.cpp | 11 ++++++++--- 6 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 src/util/compat.hpp diff --git a/src/algorithms.hpp b/src/algorithms.hpp index 43893b31..9a3ecdf7 100644 --- a/src/algorithms.hpp +++ b/src/algorithms.hpp @@ -29,7 +29,7 @@ typename util::sequence_traits<C>::value_type sum(C const& c) { using value_type = typename util::sequence_traits<C>::value_type; - return std::accumulate(std::begin(c), std::end(c), value_type{0}); + return std::accumulate(util::cbegin(c), util::cend(c), value_type{0}); } template <typename C> diff --git a/src/util/compat.hpp b/src/util/compat.hpp new file mode 100644 index 00000000..c288effe --- /dev/null +++ b/src/util/compat.hpp @@ -0,0 +1,37 @@ +#pragma once + +/* Collection of compatibility workarounds to deal with compiler defects */ + +#include <cstddef> +#include <cmath> + +namespace compat { + +// std::end() broken with (at least) xlC 13.1.4. + +template <typename T> +auto end(T& x) -> decltype(x.end()) { return x.end(); } + +template <typename T, std::size_t N> +T* end(T (&x)[N]) { return &x[0]+N; } + +template <typename T, std::size_t N> +const T* end(const T (&x)[N]) { return &x[0]+N; } + +// workaround bad optimization reordering in xlC 13.1.4 + +inline void compiler_barrier_if_xlc_leq(unsigned ver) { +#if defined(__xlC__) + if (__xlC__<=ver) { + asm volatile ("" ::: "memory"); + } +#endif +} + +// Work around bad ordering of std::isinf() (sometimes) within switch, xlC 13.1.4; +// wrapping the call within another function appears to be sufficient. + +template <typename X> +inline constexpr bool isinf(X x) { return std::isinf(x); } + +} diff --git a/src/util/iterutil.hpp b/src/util/iterutil.hpp index 8c327d41..00e52d62 100644 --- a/src/util/iterutil.hpp +++ b/src/util/iterutil.hpp @@ -10,6 +10,7 @@ #include <type_traits> #include <utility> +#include <util/compat.hpp> #include <util/meta.hpp> namespace nest { @@ -78,7 +79,8 @@ auto front(Seq& seq) -> decltype(*std::begin(seq)) { template <typename Seq> auto back(Seq& seq) -> decltype(*std::begin(seq)) { - return *upto(std::begin(seq), std::end(seq)); + // COMPAT: use own `end` implementation to work around xlC 13.1 bug. + return *upto(std::begin(seq), compat::end(seq)); } /* diff --git a/src/util/meta.hpp b/src/util/meta.hpp index 3bc8d941..34b809bb 100644 --- a/src/util/meta.hpp +++ b/src/util/meta.hpp @@ -6,6 +6,8 @@ #include <iterator> #include <type_traits> +#include <util/compat.hpp> + namespace nest { namespace mc { namespace util { @@ -36,8 +38,9 @@ constexpr auto cbegin(const T& c) -> decltype(std::begin(c)) { } template <typename T> -constexpr auto cend(const T& c) -> decltype(std::end(c)) { - return std::end(c); +constexpr auto cend(const T& c) -> decltype(compat::end(c)) { + // COMPAT: use own `end` implementation to work around xlC 13.1 bug. + return compat::end(c); } template <typename T> diff --git a/src/util/uninitialized.hpp b/src/util/uninitialized.hpp index a13842ac..f9c1bf36 100644 --- a/src/util/uninitialized.hpp +++ b/src/util/uninitialized.hpp @@ -12,6 +12,7 @@ #include <type_traits> #include <utility> +#include "util/compat.hpp" #include "util/meta.hpp" namespace nest { @@ -33,8 +34,18 @@ public: using reference = X&; using const_reference= const X&; - pointer ptr() { return static_cast<X*>(static_cast<void*>(&data)); } - const_pointer cptr() const { return static_cast<const X*>(static_cast<const void*>(&data)); } + pointer ptr() { + // COMPAT: xlC 13.1.4 workaround: + // should be equivalent to `return reinterpret_cast<X*>(&data)`. + compat::compiler_barrier_if_xlc_leq(0x0d01); + return static_cast<X*>(static_cast<void*>(&data)); + } + const_pointer cptr() const { + // COMPAT: xlC 13.1.4 workaround: + // should be equivalent to `return reinterpret_cast<const X*>(&data)` + compat::compiler_barrier_if_xlc_leq(0x0d01); + return static_cast<const X*>(static_cast<const void*>(&data)); + } reference ref() { return *ptr(); } const_reference cref() const { return *cptr(); } diff --git a/tests/unit/test_math.cpp b/tests/unit/test_math.cpp index 2bae462d..1a5f442f 100644 --- a/tests/unit/test_math.cpp +++ b/tests/unit/test_math.cpp @@ -2,7 +2,9 @@ #include <limits> #include "../gtest.h" + #include <math.hpp> +#include <util/compat.hpp> using namespace nest::mc::math; @@ -82,17 +84,20 @@ TEST(math, infinity) { // check values for float, double, long double auto finf = infinity<float>(); EXPECT_TRUE((std::is_same<float, decltype(finf)>::value)); - EXPECT_TRUE(std::isinf(finf)); + // COMPAT: use compatibility wrapper for isinf() thanks to xlC 13.1 bug. + EXPECT_TRUE(compat::isinf(finf)); EXPECT_GT(finf, 0.f); auto dinf = infinity<double>(); EXPECT_TRUE((std::is_same<double, decltype(dinf)>::value)); - EXPECT_TRUE(std::isinf(dinf)); + // COMPAT: use compatibility wrapper for isinf() thanks to xlC 13.1 bug. + EXPECT_TRUE(compat::isinf(dinf)); EXPECT_GT(dinf, 0.0); auto ldinf = infinity<long double>(); EXPECT_TRUE((std::is_same<long double, decltype(ldinf)>::value)); - EXPECT_TRUE(std::isinf(ldinf)); + // COMPAT: use compatibility wrapper for isinf() thanks to xlC 13.1 bug. + EXPECT_TRUE(compat::isinf(ldinf)); EXPECT_GT(ldinf, 0.0l); // check default value promotes correctly (i.e., acts like INFINITY) -- GitLab