Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/corecel/data/detail/FillInvalid.hh was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file corecel/data/detail/FillInvalid.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <algorithm>
0010 #include <cstring>
0011 #include <type_traits>
0012 
0013 #include "corecel/OpaqueId.hh"
0014 #include "corecel/cont/Array.hh"
0015 
0016 #include "../Collection.hh"
0017 
0018 namespace celeritas
0019 {
0020 namespace detail
0021 {
0022 //---------------------------------------------------------------------------//
0023 template<class T, class Enable = void>
0024 struct TrivialInvalidValueTraits
0025 {
0026     static_assert(std::is_trivial<T>::value,
0027                   "Cannot legally memset non-trivial types");
0028     static T value()
0029     {
0030         T result;
0031         std::memset(&result, 0xd0, sizeof(T));  // 4*b"\xf0\x9f\xa6\xa4".decode()
0032         return result;
0033     }
0034 };
0035 
0036 //---------------------------------------------------------------------------//
0037 template<class T>
0038 struct TrivialInvalidValueTraits<
0039     T,
0040     typename std::enable_if<std::is_arithmetic<T>::value>::type>
0041 {
0042     static constexpr T value() { return std::numeric_limits<T>::max() / 2; }
0043 };
0044 
0045 template<class T, size_type N>
0046 struct TrivialInvalidValueTraits<Array<T, N>, void>
0047 {
0048     static Array<T, N> value()
0049     {
0050         Array<T, N> result;
0051         result.fill(TrivialInvalidValueTraits<T>::value());
0052         return result;
0053     }
0054 };
0055 
0056 //---------------------------------------------------------------------------//
0057 /*!
0058  * Return an 'invalid' value.
0059  *
0060  * This is used to reproducibly replicate construction on device, where
0061  * {cuda,hip}Malloc doesn't call the default constructors on data.
0062  *
0063  * Instead of assigning 'NaN', which may work automatically for sentinel logic
0064  * such as "valid if x > 0)", we assign large (half-max) values for numeric
0065  * types, and fill trivial types with garbage values that look like
0066  * `0xd0d0d0d0`.
0067  */
0068 template<class T, class Enable = void>
0069 struct InvalidValueTraits
0070 {
0071     static T value()
0072     {
0073 #if !CELERITAS_USE_HIP
0074         static_assert(std::is_trivially_copyable<T>::value,
0075                       "Filling can only be done to trivially copyable "
0076                       "classes");
0077 #endif
0078         // BAD: we're assigning garbage data to a result with a *non-trivial
0079         // type*, such as a struct of OpaqueIds. However, this is in essence
0080         // what's going on when we allocate space for nontrivial types on
0081         // device: whatever's there (whether memset on NVIDIA or uninitialized
0082         // on AMD) is not going to have "placement new" applied since we're not
0083         // using thrust or calling Filler to launch initialization kernels on
0084         // all our datatypes. Reinterpret the data as bytes and assign garbage
0085         // values.
0086         T result;
0087         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
0088         std::memset(reinterpret_cast<unsigned char*>(&result), 0xd0, sizeof(T));
0089         return result;
0090     }
0091 };
0092 
0093 //---------------------------------------------------------------------------//
0094 template<class I, class T>
0095 struct InvalidValueTraits<OpaqueId<I, T>, void>
0096 {
0097     static constexpr OpaqueId<I, T> value()
0098     {
0099         return OpaqueId<I, T>(std::numeric_limits<T>::max() / 2);
0100     }
0101 };
0102 
0103 //---------------------------------------------------------------------------//
0104 template<class T>
0105 struct InvalidValueTraits<T, typename std::enable_if<std::is_trivial<T>::value>::type>
0106 {
0107     static T value() { return TrivialInvalidValueTraits<T>::value(); }
0108 };
0109 
0110 //---------------------------------------------------------------------------//
0111 /*!
0112  * Fill a collection with an invalid value (nullop if not host/mapped).
0113  */
0114 template<MemSpace M>
0115 struct InvalidFiller
0116 {
0117     template<class T, Ownership W, class I>
0118     void operator()(Collection<T, W, M, I>* c)
0119     {
0120         CELER_EXPECT(c);
0121 
0122         T val = InvalidValueTraits<T>::value();
0123         auto items = (*c)[AllItems<T, M>{}];
0124         std::fill(items.begin(), items.end(), val);
0125     }
0126 };
0127 
0128 template<>
0129 struct InvalidFiller<MemSpace::device>
0130 {
0131     template<class T>
0132     void operator()(T*)
0133     {
0134         /* Null-op */
0135     }
0136 };
0137 
0138 //---------------------------------------------------------------------------//
0139 /*!
0140  * Fill a collection with an invalid value (host only).
0141  *
0142  * This can probably be removed once we switch to C++17 and \c
0143  * CollectionBuilder::resize uses \code if constexpr \endcode .
0144  */
0145 template<class T, Ownership W, MemSpace M, class I = ItemId<T>>
0146 void fill_invalid(Collection<T, W, M, I>* c)
0147 {
0148     return InvalidFiller<M>{}(c);
0149 }
0150 
0151 //---------------------------------------------------------------------------//
0152 }  // namespace detail
0153 }  // namespace celeritas