Vector expression templates bug - behaves like reference to expired temporary
Hi, I encountered problem when using simdpp with auto type deduction to allow usage of expression templates. The problem appears both on scalar fallback implementation and on SIMDPP_ARCH_X86_AVX2.
This is the minimal working example that I managed to write, that causes problems; rx and mrx have random values in each run. They should be all 0s and all 1s respectively.
constexpr int N = SIMDPP_FAST_FLOAT32_SIZE;
SIMDPP_ALIGN(simdpp::float32<N>::length_bytes) float x_idx[N];
for (int i = 0; i < N; i++) {
x_idx[i] = static_cast<float>(i);
}
auto x = simdpp::load<simdpp::float32<N>>(x_idx);
auto xi = simdpp::to_int32(x);
auto rx = x - simdpp::to_float32(xi);
auto mrx = simdpp::splat<simdpp::float32<N>>(1.0f) - rx;
Here is the whole example with a to_string function used to store and display a vector:
#define SIMDPP_ARCH_X86_AVX2
#include <simdpp/simd.h>
#include <iostream>
#include <cstdint>
#include <sstream>
template <typename V>
std::string to_string(const V& v)
{
using T = typename decltype(v.eval())::element_type;
constexpr auto N = V::length;
SIMDPP_ALIGN(V::length_bytes) T data[N];
simdpp::store(data, v);
std::ostringstream os;
os << "[";
for (size_t i = 0; i < N - 1; i++) {
os << data[i] << ", ";
}
os << data[N - 1] << "]";
return os.str();
}
int main() {
constexpr int N = SIMDPP_FAST_FLOAT32_SIZE;
SIMDPP_ALIGN(simdpp::float32<N>::length_bytes) float x_idx[N];
for (int i = 0; i < N; i++) {
x_idx[i] = static_cast<float>(i);
}
auto x = simdpp::load<simdpp::float32<N>>(x_idx);
auto xi = simdpp::to_int32(x);
auto rx = x - simdpp::to_float32(xi);
auto mrx = simdpp::splat<simdpp::float32<N>>(1.0f) - rx;
std::cout << "x: " << to_string(x) << std::endl;
std::cout << "xi: " << to_string(xi) << std::endl;
std::cout << "rx: " << to_string(rx) << std::endl;
std::cout << "mrx: " << to_string(mrx) << std::endl;
return 0;
}
Tested with master branch, commit: dbdb146eb569033d46f7d06beba92d7c00ee3b89.
Forcing the evaluation did not help (this does not work):
auto x = simdpp::load<simdpp::float32<N>>(x_idx).eval();
auto xi = simdpp::to_int32(x).eval();
auto rx = x - simdpp::to_float32(xi).eval();
auto mrx = (simdpp::splat<simdpp::float32<N>>(1.0f) - rx).eval();
The problem disappears when I use explicit Regular vector types, that is (this gives proper results):
simdpp::float32<N> x = simdpp::load<simdpp::float32<N>>(x_idx);
simdpp::int32<N> xi = simdpp::to_int32(x);
simdpp::float32<N> rx = x - simdpp::to_float32(xi);
simdpp::float32<N> mrx = simdpp::splat<simdpp::float32<N>>(1.0f) - rx;
Another fix that helped in this case was to modify libsimdpp/simdpp/expr.h and change all expression structures to use const values instead of const references for sub-expressions. For example from
template<class E1, class E2>
struct expr_bit_and {
const E1& a;
const E2& b;
};
to
template<class E1, class E2>
struct expr_bit_and {
const E1 a;
const E2 b;
};
It looks a bit like somewhere the expression stores reference to temporary and uses it after it is expired.
Best regards, Krzysztof.