blob: 198aac43fd787bdf1c977b9a1679fb7f2a07e656 [file] [log] [blame]
/*
* Copyright (c) 2014 The Native Client Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/*
* Code that's reused in vector tests.
*/
#ifndef _NATIVE_CLIENT_TESTS_SIMD_VECTOR_H
#define _NATIVE_CLIENT_TESTS_SIMD_VECTOR_H 1
#include <cmath>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <limits>
#include <pthread.h>
#include <random>
#include <type_traits>
#define CHECK(EXPR) \
do { \
if (!(EXPR)) { \
std::cerr << #EXPR " returned false at line " << __LINE__ << '\n'; \
exit(1); \
} \
} while (0)
#define NOINLINE __attribute__((noinline))
// X-Macro invoking ACTION(NUM_ELEMENTS, ELEMENT_TYPE, FORMAT_AS) for each
// supported vector type.
//
// TODO(jfb) Add uint64_t, double and wider vectors once supported.
#define FOR_EACH_VECTOR_TYPE(ACTION) \
ACTION(16, uint8_t, uint32_t) \
ACTION(8, uint16_t, uint16_t) \
ACTION(4, uint32_t, uint32_t) \
ACTION(4, float, float)
// X-Macro for each state of the test.
#define FOR_EACH_STATE(ACTION) \
ACTION(SETUP) ACTION(TEST) ACTION(CHECK) ACTION(CLEANUP)
struct Test {
typedef void *(*Func)(void *); // POSIX thread function signature.
#define CREATE_ENUM(S) S,
enum State { FOR_EACH_STATE(CREATE_ENUM) DONE };
Func functions[DONE]; // One function per state.
enum { InBuf0, InBuf1, OutBuf, NumBufs };
void *bufs[NumBufs];
const char *name;
};
#define CREATE_STRING(S) #S,
static const char *states[] = {FOR_EACH_STATE(CREATE_STRING)};
#define DEFINE_FORMAT(N, T, FMT) \
FMT format(T v) { return v; }
FOR_EACH_VECTOR_TYPE(DEFINE_FORMAT)
template <size_t NumElements, typename T>
NOINLINE void print_vector(const T *vec) {
std::cout << '{';
for (size_t i = 0; i != NumElements; ++i)
std::cout << format(vec[i]) << (i == NumElements - 1 ? '}' : ',');
}
// Declare vector types of a particular number of elements and underlying type,
// aligned to the element width (*not* the vector's width).
//
// Attributes must be integer constants, work around that limitation and allow
// using the following type for vectors within generic code:
//
// typedef typename VectorHelper<NumElements, T>::Vector VectorAligned;
template <size_t NumElements, typename T>
struct VectorHelper;
#define SPECIALIZE_VECTOR_HELPER(N, T, FMT) \
template <> \
struct VectorHelper<N, T> { \
typedef T VectorAligned __attribute__((vector_size(N * sizeof(T)))); \
typedef T Unaligned \
__attribute__((vector_size(N * sizeof(T)), aligned(1))); \
typedef T ElementAligned \
__attribute__((vector_size(N * sizeof(T)), aligned(sizeof(T)))); \
};
FOR_EACH_VECTOR_TYPE(SPECIALIZE_VECTOR_HELPER)
// Fill an array with a pseudo-random sequence.
//
// Always generate the same pseudo-random sequence from one run to another (to
// preserve golden output file), but different sequence for each type being
// tested.
typedef std::minstd_rand Rng;
template <typename T>
NOINLINE void pseudoRandomInit(Rng *r, T *buf, size_t size) {
for (size_t i = 0; i != size; ++i)
buf[i] = std::is_floating_point<T>::value ? (*r)() / (T)(*r).max() : (*r)();
}
// Define main() and run all the tests in the TEST_ARRAY, which is defined as:
// volatile Test TESTS_ARRAY[] = { ... };
#define DEFINE_MAIN(TESTS_ARRAY) \
int main(void) { \
std::cout << std::dec << std::fixed \
<< std::setprecision(std::numeric_limits<double>::digits10); \
\
for (int state = Test::SETUP; state != Test::DONE; ++state) \
for (size_t i = 0; i != sizeof(tests) / sizeof(tests[0]); ++i) { \
int err; \
pthread_t tid; \
volatile Test *test = &TESTS_ARRAY[i]; \
Test::Func f = test->functions[state]; \
std::cout << states[state] << ' ' << test->name << '\n'; \
\
err = pthread_create(&tid, NULL, f, (void *)test); \
CHECK(err == 0); \
err = pthread_join(tid, NULL); \
CHECK(err == 0); \
} \
\
return 0; \
}
#endif