Tests: Stage packetmath FP-exception operands through volatile objects libeigen/eigen!3359 Co-authored-by: Rasmus Munk Larsen <rmlarsen@gmail.com>
diff --git a/test/packetmath.cpp b/test/packetmath.cpp index 7b73f1d..b7dc1e1 100644 --- a/test/packetmath.cpp +++ b/test/packetmath.cpp
@@ -2177,23 +2177,28 @@ } // The FE_DIVBYZERO, FE_INVALID and FE_OVERFLOW flags raised by op(operands, results), where operands are copies of -// in[0, NumIn) and results are copied to out[0, NumOut). The copies go through volatile bytes after feholdexcept and -// before fetestexcept, which keeps the compiler from folding op or moving it across the flag accesses: glibc marks -// those functions leaf, so data that only escapes through a pointer may be moved across them. +// in[0, NumIn) and results are copied to out[0, NumOut). The operands are read from, and the results written to, +// volatile objects after feholdexcept and before fetestexcept, which keeps the compiler from folding op or moving it +// across the flag accesses: glibc marks those functions leaf, so data that only escapes through a pointer may be moved +// across them. The objects themselves must be volatile: GCC 10 at -O3 drops volatile-qualified accesses to the +// caller's non-volatile arrays, and with them an op whose results the caller never reads. template <int NumIn, int NumOut, typename Scalar, typename Op> int raised_exceptions(const Scalar* in, Scalar* out, Op op) { Scalar operands[NumIn], results[NumOut]; + volatile unsigned char staged_operands[sizeof(operands)], staged_results[sizeof(results)]; + const unsigned char* in_bytes = reinterpret_cast<const unsigned char*>(in); + for (std::size_t k = 0; k < sizeof(operands); ++k) staged_operands[k] = in_bytes[k]; std::fenv_t saved_environment; if (std::feholdexcept(&saved_environment) != 0) return 0; - const volatile unsigned char* in_bytes = reinterpret_cast<const volatile unsigned char*>(in); unsigned char* operand_bytes = reinterpret_cast<unsigned char*>(operands); - for (std::size_t k = 0; k < sizeof(operands); ++k) operand_bytes[k] = in_bytes[k]; + for (std::size_t k = 0; k < sizeof(operands); ++k) operand_bytes[k] = staged_operands[k]; op(operands, results); const unsigned char* result_bytes = reinterpret_cast<const unsigned char*>(results); - volatile unsigned char* out_bytes = reinterpret_cast<volatile unsigned char*>(out); - for (std::size_t k = 0; k < sizeof(results); ++k) out_bytes[k] = result_bytes[k]; + for (std::size_t k = 0; k < sizeof(results); ++k) staged_results[k] = result_bytes[k]; const int raised = std::fetestexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); std::fesetenv(&saved_environment); + unsigned char* out_bytes = reinterpret_cast<unsigned char*>(out); + for (std::size_t k = 0; k < sizeof(results); ++k) out_bytes[k] = staged_results[k]; return raised; }