libsimdpp icon indicating copy to clipboard operation
libsimdpp copied to clipboard

Vector expression templates bug - behaves like reference to expired temporary

Open klecki opened this issue 7 years ago • 0 comments

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.

klecki avatar Oct 03 '18 10:10 klecki