Low-Level Abstraction of Memory Access
Proofs.hpp
Go to the documentation of this file.
1 // Copyright 2022 Bernhard Manfred Gruber
2 // SPDX-License-Identifier: MPL-2.0
3 
4 #pragma once
5 
6 #include "ArrayIndexRange.hpp"
7 #include "Core.hpp"
8 
9 namespace llama
10 {
11 // FIXME(bgruber): this test is actually not correct, because __cpp_constexpr_dynamic_alloc only guarantees constexpr
12 // std::allocator
13 #ifdef __cpp_constexpr_dynamic_alloc
14  namespace internal
15  {
16  template<typename T>
17  struct DynArray
18  {
19  constexpr DynArray() = default;
20 
21  constexpr explicit DynArray(std::size_t n) : data(new T[n]{})
22  {
23  }
24 
25  DynArray(const DynArray&) = delete;
26  DynArray(DynArray&&) = delete;
27  auto operator=(const DynArray&) -> DynArray& = delete;
28  auto operator=(DynArray&&) -> DynArray& = delete;
29 
30  constexpr ~DynArray()
31  {
32  delete[] data;
33  }
34 
35  constexpr void resize(std::size_t n)
36  {
37  delete[] data;
38  data = new T[n]{};
39  }
40 
41  T* data = nullptr; // TODO(bgruber): replace by std::unique_ptr in C++23
42  };
43  } // namespace internal
44 
46  // Unfortunately, this only works for smallish array dimensions, because of compiler limits on constexpr evaluation
47  // depth.
49  template<typename Mapping>
50  constexpr auto mapsNonOverlappingly(const Mapping& m) -> bool
51  {
52  internal::DynArray<internal::DynArray<std::uint64_t>> blobByteMapped(m.blobCount);
53  for(std::size_t i = 0; i < m.blobCount; i++)
54  blobByteMapped.data[i].resize(divCeil(m.blobSize(i), std::size_t{64}));
55 
56  auto testAndSet = [&](auto blob, auto offset) constexpr
57  {
58  const auto bit = std::uint64_t{1} << (offset % 64);
59  if(blobByteMapped.data[blob].data[offset / 64] & bit)
60  return true;
61  blobByteMapped.data[blob].data[offset / 64] |= bit;
62  return false;
63  };
64 
65  bool collision = false;
66  forEachLeafCoord<typename Mapping::RecordDim>(
67  [&](auto rc) constexpr
68  {
69  if(collision)
70  return;
71  for(auto ai : ArrayIndexRange{m.extents()})
72  {
73  using Type = GetType<typename Mapping::RecordDim, decltype(rc)>;
74  const auto [blob, offset] = m.blobNrAndOffset(ai, rc);
75  for(std::size_t b = 0; b < sizeof(Type); b++)
76  if(testAndSet(blob, offset + b))
77  {
78  collision = true;
79  break;
80  }
81  }
82  });
83  return !collision;
84  }
85 #endif
86 
89  // Unfortunately, this only works for smallish array dimensions, because of compiler limits on constexpr evaluation
90  // depth.
92  template<std::size_t PieceLength, typename Mapping>
93  constexpr auto mapsPiecewiseContiguous(const Mapping& m) -> bool
94  {
95  bool collision = false;
96  forEachLeafCoord<typename Mapping::RecordDim>(
97  [&](auto rc) constexpr
98  {
99  std::size_t flatIndex = 0;
100  std::size_t lastBlob = std::numeric_limits<std::size_t>::max();
101  std::size_t lastOffset = std::numeric_limits<std::size_t>::max();
102  for(auto ai : ArrayIndexRange{m.extents()})
103  {
104  using Type = GetType<typename Mapping::RecordDim, decltype(rc)>;
105  const auto [blob, offset] = m.blobNrAndOffset(ai, rc);
106  if(flatIndex % PieceLength != 0 && (lastBlob != blob || lastOffset + sizeof(Type) != offset))
107  {
108  collision = true;
109  break;
110  }
111  lastBlob = blob;
112  lastOffset = offset;
113  flatIndex++;
114  }
115  });
116  return !collision;
117  }
118 } // namespace llama
#define LLAMA_EXPORT
Definition: macros.hpp:192
constexpr auto mapsPiecewiseContiguous(const Mapping &m) -> bool
Definition: Proofs.hpp:93
constexpr auto divCeil(Integral a, Integral b) -> Integral
Returns the ceiling of a / b.
Definition: Core.hpp:570
typename internal::GetTypeImpl< RecordDim, RecordCoordOrTags... >::type GetType
Definition: Core.hpp:388
Range allowing to iterate over all indices in an ArrayExtents.