mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-09-08 13:23:51 +00:00
Merge topic 'fix-nightly-gcc5-cuda'
0182eb9d9 Test variant arguments to worklets de28a4351 Add an entry to VariantUnion to help compiler copy structs Acked-by: Kitware Robot <kwrobot@kitware.com> Acked-by: Vicente Bolea <vicente.bolea@kitware.com> Merge-request: !3030
This commit is contained in:
commit
2f75729cd7
70
docs/changelog/fix-nightly-gcc5-cuda.md
Normal file
70
docs/changelog/fix-nightly-gcc5-cuda.md
Normal file
@ -0,0 +1,70 @@
|
||||
# Fixed issue with trivial variant copies
|
||||
|
||||
A rare error occurred with trivial copies of variants. The problem is likely
|
||||
a compiler bug, and has so far only been observed when passing the variant
|
||||
to a CUDA kernel when compiling with GCC 5.
|
||||
|
||||
The problem was caused by structures with padding. `struct` objects in
|
||||
C/C++ are frequently padded with unused memory to align all entries
|
||||
properly. For example, consider the following simple `struct`.
|
||||
|
||||
``` cpp
|
||||
struct FooHasPadding
|
||||
{
|
||||
vtkm::Int32 A;
|
||||
// Padding here.
|
||||
vtkm::Int64 C;
|
||||
};
|
||||
```
|
||||
|
||||
Because the `C` member is a 64-bit integer, it needs to be aligned on
|
||||
8-byte (i.e., 64-bit) address locations. For this to work, the C++ compiler
|
||||
adds 4 bytes of padding between `A` and `C` so that an array of
|
||||
`FooHasPadding`s will have the `C` member always on an 8-byte boundary.
|
||||
|
||||
Now consider a second `struct` that is similar to the first but has a valid
|
||||
member where the padding would be.
|
||||
|
||||
``` cpp
|
||||
struct BarNoPadding
|
||||
{
|
||||
vtkm::Int32 A;
|
||||
vtkm::Int32 B;
|
||||
vtkm::Int64 C;
|
||||
};
|
||||
```
|
||||
|
||||
This structure does not need padding because the `A` and `B` members
|
||||
combine to fill the 8 bytes that `C` needs for the alignment. Both
|
||||
`FooHasPadding` and `BarNoPadding` fill 16 bytes of memory. The `A` and `C`
|
||||
members are at the same offsets, respectively, for the two structures. The
|
||||
`B` member happens to reside just where the padding is for `FooHasPadding`.
|
||||
|
||||
Now, let's say we create a `vtkm::exec::Variant<FooHasPadding, BarNoPadding>`.
|
||||
Internally, the `Variant` class holds a union that looks roughly like the
|
||||
following.
|
||||
|
||||
``` cpp
|
||||
union VariantUnion
|
||||
{
|
||||
FooHasPadding V0;
|
||||
BarNoPadding V1;
|
||||
};
|
||||
```
|
||||
|
||||
This is a perfectly valid use of a `union`. We just need to keep track of
|
||||
which type of object is in it (which the `Variant` object does for you).
|
||||
|
||||
The problem appeared to occur when `VariantUnion` contained a
|
||||
`BarNoPadding` and was passed from the host to the device via an argument
|
||||
to a global function. The compiler must notice that the first type
|
||||
(`FooHasPadding`) is the "biggest" and uses that for trivial copies (which
|
||||
just copy bytes like `memcpy`). Since it's using `FooHasPadding` as its
|
||||
prototype for the byte copy, and accidentally skips over padded regions that
|
||||
are valid when the `union` contains a `BarNoPadding`. This appears to be a
|
||||
compiler bug. (At least, I cannot find a reason why this is encroaching
|
||||
undefined behavior.)
|
||||
|
||||
The solution adds a new, unused type to the internal `union` for `Variant`
|
||||
that is an object as large as the largest entry in the union and contains
|
||||
no padding.
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <vtkmstd/is_trivial.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
@ -51,6 +52,58 @@ template <typename... Ts>
|
||||
using AllTriviallyDestructible =
|
||||
vtkm::ListAll<vtkm::List<Ts...>, vtkmstd::is_trivially_destructible>;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Helper functions to determine the maximum type size.
|
||||
#if defined(VTKM_GCC) && (__GNUC__ == 5)
|
||||
// GCC5 gives an error with `sizeof(Ts)...` for an unexpanded parameter pack.
|
||||
template <typename T0>
|
||||
constexpr std::size_t MaxSizeOf()
|
||||
{
|
||||
return sizeof(T0);
|
||||
}
|
||||
template <typename T0, typename T1, typename... Ts>
|
||||
constexpr std::size_t MaxSizeOf()
|
||||
{
|
||||
return std::max(sizeof(T0), MaxSizeOf<T1, Ts...>());
|
||||
}
|
||||
#else
|
||||
template <typename... Ts>
|
||||
constexpr std::size_t MaxSizeOf()
|
||||
{
|
||||
return std::max({ sizeof(Ts)... });
|
||||
}
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Placeholder for a fully used structure of the given type.
|
||||
template <std::size_t Size, bool = (Size > 8)>
|
||||
struct SizedPlaceholder
|
||||
{
|
||||
VTKM_STATIC_ASSERT(Size > 0);
|
||||
vtkm::Int8 A;
|
||||
SizedPlaceholder<Size - 1> B;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SizedPlaceholder<1, false>
|
||||
{
|
||||
vtkm::Int8 A;
|
||||
};
|
||||
|
||||
template <std::size_t Size>
|
||||
struct SizedPlaceholder<Size, true>
|
||||
{
|
||||
vtkm::Int8 A;
|
||||
vtkm::Int8 B;
|
||||
vtkm::Int8 C;
|
||||
vtkm::Int8 D;
|
||||
vtkm::Int8 E;
|
||||
vtkm::Int8 F;
|
||||
vtkm::Int8 G;
|
||||
vtkm::Int8 H;
|
||||
SizedPlaceholder<Size - 8> I;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
@ -94,6 +147,11 @@ union VariantUnionNTD;
|
||||
template <typename T0>
|
||||
union VariantUnionTD<T0>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
|
||||
VariantUnionTD() = default;
|
||||
@ -101,6 +159,11 @@ union VariantUnionTD<T0>
|
||||
template <typename T0>
|
||||
union VariantUnionNTD<T0>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
|
||||
VariantUnionNTD() = default;
|
||||
@ -110,6 +173,11 @@ union VariantUnionNTD<T0>
|
||||
template <typename T0, typename T1>
|
||||
union VariantUnionTD<T0, T1>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
|
||||
@ -118,6 +186,11 @@ union VariantUnionTD<T0, T1>
|
||||
template <typename T0, typename T1>
|
||||
union VariantUnionNTD<T0, T1>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
|
||||
@ -128,6 +201,11 @@ union VariantUnionNTD<T0, T1>
|
||||
template <typename T0, typename T1, typename T2>
|
||||
union VariantUnionTD<T0, T1, T2>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
@ -137,6 +215,11 @@ union VariantUnionTD<T0, T1, T2>
|
||||
template <typename T0, typename T1, typename T2>
|
||||
union VariantUnionNTD<T0, T1, T2>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
@ -148,6 +231,11 @@ union VariantUnionNTD<T0, T1, T2>
|
||||
template <typename T0, typename T1, typename T2, typename T3>
|
||||
union VariantUnionTD<T0, T1, T2, T3>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
@ -158,6 +246,11 @@ union VariantUnionTD<T0, T1, T2, T3>
|
||||
template <typename T0, typename T1, typename T2, typename T3>
|
||||
union VariantUnionNTD<T0, T1, T2, T3>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
@ -170,6 +263,11 @@ union VariantUnionNTD<T0, T1, T2, T3>
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4>
|
||||
union VariantUnionTD<T0, T1, T2, T3, T4>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
@ -181,6 +279,11 @@ union VariantUnionTD<T0, T1, T2, T3, T4>
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4>
|
||||
union VariantUnionNTD<T0, T1, T2, T3, T4>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
@ -194,6 +297,11 @@ union VariantUnionNTD<T0, T1, T2, T3, T4>
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
|
||||
union VariantUnionTD<T0, T1, T2, T3, T4, T5>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
@ -206,6 +314,11 @@ union VariantUnionTD<T0, T1, T2, T3, T4, T5>
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
|
||||
union VariantUnionNTD<T0, T1, T2, T3, T4, T5>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
@ -220,6 +333,11 @@ union VariantUnionNTD<T0, T1, T2, T3, T4, T5>
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
|
||||
union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5, T6>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
@ -233,6 +351,11 @@ union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6>
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
|
||||
union VariantUnionNTD<T0, T1, T2, T3, T4, T5, T6>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5, T6>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
@ -248,6 +371,11 @@ union VariantUnionNTD<T0, T1, T2, T3, T4, T5, T6>
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
|
||||
union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5, T6, T7>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
@ -262,6 +390,11 @@ union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
|
||||
union VariantUnionNTD<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5, T6, T7>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
@ -279,6 +412,11 @@ union VariantUnionNTD<T0, T1, T2, T3, T4, T5, T6, T7>
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename... Ts>
|
||||
union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
@ -296,6 +434,11 @@ union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...>
|
||||
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename... Ts>
|
||||
union VariantUnionNTD<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...>()> Placeholder;
|
||||
|
||||
T0 V0;
|
||||
T1 V1;
|
||||
T2 V2;
|
||||
|
@ -36,6 +36,7 @@ $# Ignore the following comment. It is meant for the generated file.
|
||||
|
||||
#include <vtkmstd/is_trivial.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
$py(max_expanded=8)\
|
||||
@ -84,6 +85,58 @@ template <typename... Ts>
|
||||
using AllTriviallyDestructible =
|
||||
vtkm::ListAll<vtkm::List<Ts...>, vtkmstd::is_trivially_destructible>;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Helper functions to determine the maximum type size.
|
||||
#if defined(VTKM_GCC) && (__GNUC__ == 5)
|
||||
// GCC5 gives an error with `sizeof(Ts)...` for an unexpanded parameter pack.
|
||||
template <typename T0>
|
||||
constexpr std::size_t MaxSizeOf()
|
||||
{
|
||||
return sizeof(T0);
|
||||
}
|
||||
template <typename T0, typename T1, typename... Ts>
|
||||
constexpr std::size_t MaxSizeOf()
|
||||
{
|
||||
return std::max(sizeof(T0), MaxSizeOf<T1, Ts...>());
|
||||
}
|
||||
#else
|
||||
template <typename... Ts>
|
||||
constexpr std::size_t MaxSizeOf()
|
||||
{
|
||||
return std::max({ sizeof(Ts)... });
|
||||
}
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Placeholder for a fully used structure of the given type.
|
||||
template <std::size_t Size, bool = (Size > 8)>
|
||||
struct SizedPlaceholder
|
||||
{
|
||||
VTKM_STATIC_ASSERT(Size > 0);
|
||||
vtkm::Int8 A;
|
||||
SizedPlaceholder<Size - 1> B;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SizedPlaceholder<1, false>
|
||||
{
|
||||
vtkm::Int8 A;
|
||||
};
|
||||
|
||||
template <std::size_t Size>
|
||||
struct SizedPlaceholder<Size, true>
|
||||
{
|
||||
vtkm::Int8 A;
|
||||
vtkm::Int8 B;
|
||||
vtkm::Int8 C;
|
||||
vtkm::Int8 D;
|
||||
vtkm::Int8 E;
|
||||
vtkm::Int8 F;
|
||||
vtkm::Int8 G;
|
||||
vtkm::Int8 H;
|
||||
SizedPlaceholder<Size - 8> I;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
@ -128,6 +181,11 @@ $for(param_length in range(max_expanded))\
|
||||
template <$typename_list(param_length)>
|
||||
union VariantUnionTD<$type_list(param_length)>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<$type_list(param_length)>()> Placeholder;
|
||||
|
||||
$for(param_index in range(param_length + 1))\
|
||||
T$(param_index) V$(param_index);
|
||||
$endfor\
|
||||
@ -137,6 +195,11 @@ $endfor\
|
||||
template <$typename_list(param_length)>
|
||||
union VariantUnionNTD<$type_list(param_length)>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<$type_list(param_length)>()> Placeholder;
|
||||
|
||||
$for(param_index in range(param_length + 1))\
|
||||
T$(param_index) V$(param_index);
|
||||
$endfor\
|
||||
@ -150,6 +213,11 @@ $endfor\
|
||||
template <$typename_list(max_expanded), typename... Ts>
|
||||
union VariantUnionTD<$type_list(max_expanded), Ts...>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<$type_list(max_expanded), Ts...>()> Placeholder;
|
||||
|
||||
$for(param_index in range(max_expanded))\
|
||||
T$(param_index) V$(param_index);
|
||||
$endfor\
|
||||
@ -162,6 +230,11 @@ $endfor\
|
||||
template <$typename_list(max_expanded), typename... Ts>
|
||||
union VariantUnionNTD<$type_list(max_expanded), Ts...>
|
||||
{
|
||||
// Work around issue where CUDA sometimes seems to miss initializing some struct members
|
||||
// if another entry in the varient has a struct with padding. Place an item that requires
|
||||
// everthing to be copied.
|
||||
SizedPlaceholder<MaxSizeOf<$type_list(max_expanded), Ts...>()> Placeholder;
|
||||
|
||||
$for(param_index in range(max_expanded))\
|
||||
T$(param_index) V$(param_index);
|
||||
$endfor\
|
||||
|
@ -10,13 +10,19 @@
|
||||
#include <vtkm/cont/ArrayHandle.h>
|
||||
#include <vtkm/cont/ArrayHandleIndex.h>
|
||||
#include <vtkm/cont/ExecutionObjectBase.h>
|
||||
#include <vtkm/cont/Invoker.h>
|
||||
#include <vtkm/cont/UncertainArrayHandle.h>
|
||||
|
||||
#include <vtkm/worklet/DispatcherMapField.h>
|
||||
#include <vtkm/worklet/WorkletMapField.h>
|
||||
|
||||
#include <vtkm/exec/Variant.h>
|
||||
|
||||
#include <vtkm/cont/testing/Testing.h>
|
||||
|
||||
namespace map_exec_field
|
||||
{
|
||||
|
||||
struct SimpleExecObject : vtkm::cont::ExecutionObjectBase
|
||||
{
|
||||
template <typename Device>
|
||||
@ -55,9 +61,6 @@ struct TestExecObjectWorklet
|
||||
};
|
||||
};
|
||||
|
||||
namespace map_exec_field
|
||||
{
|
||||
|
||||
static constexpr vtkm::Id ARRAY_SIZE = 10;
|
||||
|
||||
template <typename WorkletType>
|
||||
@ -105,6 +108,179 @@ struct DoTestWorklet
|
||||
}
|
||||
};
|
||||
|
||||
struct StructWithPadding
|
||||
{
|
||||
vtkm::Int32 A;
|
||||
// Padding here
|
||||
vtkm::Int64 C;
|
||||
};
|
||||
|
||||
struct StructWithoutPadding
|
||||
{
|
||||
vtkm::Int32 A;
|
||||
vtkm::Int32 B;
|
||||
vtkm::Int64 C;
|
||||
};
|
||||
|
||||
struct LargerStruct
|
||||
{
|
||||
vtkm::Int64 C;
|
||||
vtkm::Int64 D;
|
||||
vtkm::Int64 E;
|
||||
};
|
||||
|
||||
using VariantTypePadding = vtkm::exec::Variant<StructWithPadding, StructWithoutPadding>;
|
||||
using VariantTypeSizes = vtkm::exec::Variant<StructWithPadding, StructWithoutPadding, LargerStruct>;
|
||||
|
||||
struct VarientPaddingExecObj : vtkm::cont::ExecutionObjectBase
|
||||
{
|
||||
VariantTypePadding Variant;
|
||||
VTKM_CONT VariantTypePadding PrepareForExecution(const vtkm::cont::DeviceAdapterId&,
|
||||
vtkm::cont::Token&) const
|
||||
{
|
||||
return this->Variant;
|
||||
}
|
||||
};
|
||||
struct VarientSizesExecObj : vtkm::cont::ExecutionObjectBase
|
||||
{
|
||||
VariantTypeSizes Variant;
|
||||
VTKM_CONT VariantTypeSizes PrepareForExecution(const vtkm::cont::DeviceAdapterId&,
|
||||
vtkm::cont::Token&) const
|
||||
{
|
||||
return this->Variant;
|
||||
}
|
||||
};
|
||||
|
||||
struct TestVariantExecObjectPadding : vtkm::worklet::WorkletMapField
|
||||
{
|
||||
using ControlSignature = void(FieldOut a, FieldOut c, ExecObject varient);
|
||||
// Using an output field as the domain is weird, but it works.
|
||||
using InputDomain = _1;
|
||||
|
||||
VTKM_EXEC void operator()(vtkm::Int32& a, vtkm::Int64& c, const VariantTypePadding& variant) const
|
||||
{
|
||||
a = variant.Get<StructWithPadding>().A;
|
||||
c = variant.Get<StructWithPadding>().C;
|
||||
}
|
||||
};
|
||||
|
||||
struct TestVariantExecObjectNoPadding : vtkm::worklet::WorkletMapField
|
||||
{
|
||||
using ControlSignature = void(FieldOut a, FieldOut b, FieldOut c, ExecObject varient);
|
||||
// Using an output field as the domain is weird, but it works.
|
||||
using InputDomain = _1;
|
||||
|
||||
VTKM_EXEC void operator()(vtkm::Int32& a,
|
||||
vtkm::Int32& b,
|
||||
vtkm::Int64& c,
|
||||
const VariantTypePadding& variant) const
|
||||
{
|
||||
a = variant.Get<StructWithoutPadding>().A;
|
||||
b = variant.Get<StructWithoutPadding>().B;
|
||||
c = variant.Get<StructWithoutPadding>().C;
|
||||
}
|
||||
};
|
||||
|
||||
struct TestVariantExecObjectLarger : vtkm::worklet::WorkletMapField
|
||||
{
|
||||
using ControlSignature = void(FieldOut c, FieldOut d, FieldOut e, ExecObject varient);
|
||||
// Using an output field as the domain is weird, but it works.
|
||||
using InputDomain = _1;
|
||||
|
||||
VTKM_EXEC void operator()(vtkm::Int64& c,
|
||||
vtkm::Int64& d,
|
||||
vtkm::Int64& e,
|
||||
const VariantTypeSizes& variant) const
|
||||
{
|
||||
c = variant.Get<LargerStruct>().C;
|
||||
d = variant.Get<LargerStruct>().D;
|
||||
e = variant.Get<LargerStruct>().E;
|
||||
}
|
||||
};
|
||||
|
||||
void DoTestVariant()
|
||||
{
|
||||
vtkm::cont::ArrayHandle<vtkm::Int32> a;
|
||||
vtkm::cont::ArrayHandle<vtkm::Int32> b;
|
||||
vtkm::cont::ArrayHandle<vtkm::Int64> c;
|
||||
vtkm::cont::ArrayHandle<vtkm::Int64> d;
|
||||
vtkm::cont::ArrayHandle<vtkm::Int64> e;
|
||||
|
||||
// Usually you don't need to allocate output arrays, but these worklets do a
|
||||
// weird thing of using an output array as the input domain (because the
|
||||
// generative worklets have no input). It's weird to use an output field as
|
||||
// the input domain, but it works as long as you preallocate the data.
|
||||
a.Allocate(ARRAY_SIZE);
|
||||
b.Allocate(ARRAY_SIZE);
|
||||
c.Allocate(ARRAY_SIZE);
|
||||
d.Allocate(ARRAY_SIZE);
|
||||
e.Allocate(ARRAY_SIZE);
|
||||
|
||||
vtkm::cont::Invoker invoke;
|
||||
|
||||
std::cout << "Struct with Padding" << std::endl;
|
||||
{
|
||||
VarientPaddingExecObj execObject;
|
||||
execObject.Variant =
|
||||
StructWithPadding{ TestValue(0, vtkm::Int32{}), TestValue(1, vtkm::Int64{}) };
|
||||
invoke(TestVariantExecObjectPadding{}, a, c, execObject);
|
||||
auto aPortal = a.ReadPortal();
|
||||
auto cPortal = c.ReadPortal();
|
||||
for (vtkm::Id index = 0; index < ARRAY_SIZE; ++index)
|
||||
{
|
||||
VTKM_TEST_ASSERT(aPortal.Get(index) == TestValue(0, vtkm::Int32{}));
|
||||
VTKM_TEST_ASSERT(cPortal.Get(index) == TestValue(1, vtkm::Int64{}));
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Struct without Padding" << std::endl;
|
||||
{
|
||||
VarientPaddingExecObj execObject;
|
||||
execObject.Variant = StructWithoutPadding{ TestValue(2, vtkm::Int32{}),
|
||||
TestValue(3, vtkm::Int32{}),
|
||||
TestValue(4, vtkm::Int64{}) };
|
||||
invoke(TestVariantExecObjectNoPadding{}, a, b, c, execObject);
|
||||
auto aPortal = a.ReadPortal();
|
||||
auto bPortal = b.ReadPortal();
|
||||
auto cPortal = c.ReadPortal();
|
||||
// An odd bug was observed with some specific compilers. (Specifically, this was
|
||||
// last observed with GCC5 used with nvcc compiling CUDA code for the Pascal
|
||||
// architecture.) It concerned a Variant that contained 2 or more objects of the
|
||||
// same `sizeof` and the first one listed had some padding (to satisfy alignment)
|
||||
// and the second one did not. Internally, the `Variant` object constructs a
|
||||
// `union` of types in the order listed. The compiler seemed to recognize that the
|
||||
// first entry in the union was the "largest" and used that for trivial copies.
|
||||
// However, it also recognized the padding in that first object and skipped
|
||||
// copying that value even if the union was set to the second object. If that
|
||||
// condition is happening, you will probably see a failure when testing the
|
||||
// bPortal below.
|
||||
for (vtkm::Id index = 0; index < ARRAY_SIZE; ++index)
|
||||
{
|
||||
VTKM_TEST_ASSERT(aPortal.Get(index) == TestValue(2, vtkm::Int32{}));
|
||||
VTKM_TEST_ASSERT(bPortal.Get(index) == TestValue(3, vtkm::Int32{}));
|
||||
VTKM_TEST_ASSERT(cPortal.Get(index) == TestValue(4, vtkm::Int64{}));
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "LargerStruct" << std::endl;
|
||||
{
|
||||
VarientSizesExecObj execObject;
|
||||
execObject.Variant = LargerStruct{ TestValue(5, vtkm::Int64{}),
|
||||
TestValue(6, vtkm::Int64{}),
|
||||
TestValue(7, vtkm::Int64{}) };
|
||||
invoke(TestVariantExecObjectLarger{}, c, d, e, execObject);
|
||||
auto cPortal = c.ReadPortal();
|
||||
auto dPortal = d.ReadPortal();
|
||||
auto ePortal = e.ReadPortal();
|
||||
for (vtkm::Id index = 0; index < ARRAY_SIZE; ++index)
|
||||
{
|
||||
VTKM_TEST_ASSERT(cPortal.Get(index) == TestValue(5, vtkm::Int64{}));
|
||||
VTKM_TEST_ASSERT(dPortal.Get(index) == TestValue(6, vtkm::Int64{}));
|
||||
VTKM_TEST_ASSERT(ePortal.Get(index) == TestValue(7, vtkm::Int64{}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestWorkletMapFieldExecArg(vtkm::cont::DeviceAdapterId id)
|
||||
{
|
||||
std::cout << "Testing Worklet with WholeArray on device adapter: " << id.GetName() << std::endl;
|
||||
@ -112,9 +288,12 @@ void TestWorkletMapFieldExecArg(vtkm::cont::DeviceAdapterId id)
|
||||
std::cout << "--- Worklet accepting all types." << std::endl;
|
||||
vtkm::testing::Testing::TryTypes(map_exec_field::DoTestWorklet<TestExecObjectWorklet>(),
|
||||
vtkm::TypeListCommon());
|
||||
|
||||
std::cout << "--- Worklet passing variant." << std::endl;
|
||||
DoTestVariant();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
} // anonymous-ish namespace
|
||||
|
||||
int UnitTestWorkletMapFieldExecArg(int argc, char* argv[])
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user