Intel HEXL for FPGA
Intel Homomorphic Encryption FPGA Acceleration Library, accelerating the modular arithmetic operations used in homomorphic encryption.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
utils-test.hpp
Go to the documentation of this file.
1 // Copyright (C) 2020-2021 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 
4 #pragma once
5 #include <cmath>
6 #include <cstddef>
7 #include <cstdint>
8 #include <cstdlib>
9 #include <iostream>
10 #include <limits>
11 #include <memory>
12 #include <utility>
13 #include <vector>
14 
15 __extension__ typedef __int128 int128_t;
16 __extension__ typedef unsigned __int128 uint128_t;
17 
18 namespace hetest {
19 namespace utils {
20 
21 #undef TRUE // MSVC defines TRUE
22 #undef FALSE // MSVC defines FALSE
23 
26 enum class CMPINT {
27  EQ = 0,
28  LT = 1,
29  LE = 2,
30  FALSE = 3,
31  NE = 4,
32  NLT = 5,
33  NLE = 6,
34  TRUE = 7
35 };
36 
39 inline CMPINT Not(CMPINT cmp) {
40  switch (cmp) {
41  case CMPINT::EQ:
42  return CMPINT::NE;
43  case CMPINT::LT:
44  return CMPINT::NLT;
45  case CMPINT::LE:
46  return CMPINT::NLE;
47  case CMPINT::FALSE:
48  return CMPINT::TRUE;
49  case CMPINT::NE:
50  return CMPINT::EQ;
51  case CMPINT::NLT:
52  return CMPINT::LT;
53  case CMPINT::NLE:
54  return CMPINT::LE;
55  case CMPINT::TRUE:
56  return CMPINT::FALSE;
57  default:
58  return CMPINT::FALSE;
59  }
60 }
61 
62 } // namespace utils
63 } // namespace hetest
64 
65 #ifdef UTILS_DEBUG
66 
67 #define UTILS_CHECK(cond, expr) \
68  if (!(cond)) { \
69  std::cerr << expr << " in function: " << __FUNCTION__ \
70  << " in file: " __FILE__ << " at line: " << __LINE__; \
71  throw std::runtime_error("Error. Check log output"); \
72  }
73 
74 #define UTILS_CHECK_BOUNDS(arg, n, bound, expr) \
75  for (size_t utils_check_idx = 0; utils_check_idx < n; ++utils_check_idx) { \
76  UTILS_CHECK((arg)[utils_check_idx] < bound, expr); \
77  }
78 
79 #else // UTILS_DEBUG=OFF
80 
81 #define UTILS_CHECK(cond, expr) \
82  {}
83 #define UTILS_CHECK_BOUNDS(...) \
84  {}
85 
86 #endif // UTILS_DEBUG
87 
88 namespace hetest {
89 namespace utils {
90 
91 // Return x * y as 128-bit integer
92 // Correctness if x * y < 128 bits
93 inline uint128_t MultiplyUInt64(uint64_t x, uint64_t y) {
94  return uint128_t(x) * uint128_t(y);
95 }
96 
97 inline uint64_t BarrettReduce128(uint64_t input_hi, uint64_t input_lo,
98  uint64_t modulus) {
99  UTILS_CHECK(modulus != 0, "modulus == 0")
100  uint128_t n = (static_cast<uint128_t>(input_hi) << 64) |
101  (static_cast<uint128_t>(input_lo));
102 
103  return static_cast<uint64_t>(n % modulus);
104  // TODO(fboemer): actually use barrett reduction if performance-critical
105 }
106 
107 // Returns low 64bit of 128b/64b where x1=high 64b, x0=low 64b
108 inline uint64_t DivideUInt128UInt64Lo(uint64_t x1, uint64_t x0, uint64_t y) {
109  uint128_t n =
110  (static_cast<uint128_t>(x1) << 64) | (static_cast<uint128_t>(x0));
111  uint128_t q = n / y;
112 
113  return static_cast<uint64_t>(q);
114 }
115 
116 // Multiplies x * y as 128-bit integer.
117 // @param prod_hi Stores high 64 bits of product
118 // @param prod_lo Stores low 64 bits of product
119 inline void MultiplyUInt64(uint64_t x, uint64_t y, uint64_t* prod_hi,
120  uint64_t* prod_lo) {
121  uint128_t prod = MultiplyUInt64(x, y);
122  *prod_hi = static_cast<uint64_t>(prod >> 64);
123  *prod_lo = static_cast<uint64_t>(prod);
124 }
125 
126 // Return the high 128 minus BitShift bits of the 128-bit product x * y
127 template <int BitShift>
128 inline uint64_t MultiplyUInt64Hi(uint64_t x, uint64_t y) {
129  uint128_t product = MultiplyUInt64(x, y);
130  return static_cast<uint64_t>(product >> BitShift);
131 }
132 
133 // Returns most-significant bit of the input
134 inline uint64_t MSB(uint64_t input) {
135  return static_cast<uint64_t>(std::log2l(input));
136 }
137 
138 } // namespace utils
139 } // namespace hetest
140 
141 namespace hetest {
142 namespace utils {
143 
144 inline bool Compare(CMPINT cmp, uint64_t lhs, uint64_t rhs) {
145  switch (cmp) {
146  case CMPINT::EQ:
147  return lhs == rhs;
148  case CMPINT::LT:
149  return lhs < rhs;
150  break;
151  case CMPINT::LE:
152  return lhs <= rhs;
153  break;
154  case CMPINT::FALSE:
155  return false;
156  break;
157  case CMPINT::NE:
158  return lhs != rhs;
159  break;
160  case CMPINT::NLT:
161  return lhs >= rhs;
162  break;
163  case CMPINT::NLE:
164  return lhs > rhs;
165  case CMPINT::TRUE:
166  return true;
167  default:
168  return true;
169  }
170 }
171 
172 // Returns whether or not num is a power of two
173 inline bool IsPowerOfTwo(uint64_t num) { return num && !(num & (num - 1)); }
174 
175 // Returns log2(x) for x a power of 2
176 inline uint64_t Log2(uint64_t x) {
177  UTILS_CHECK(IsPowerOfTwo(x), x << " not a power of 2");
178  return MSB(x);
179 }
180 
181 // Returns the maximum value that can be represented using bits bits
182 inline uint64_t MaximumValue(uint64_t bits) {
183  UTILS_CHECK(bits <= 64, "MaximumValue requires bits <= 64; got " << bits);
184  if (bits == 64) {
185  return (std::numeric_limits<uint64_t>::max)();
186  }
187  return (1ULL << bits) - 1;
188 }
189 } // namespace utils
190 } // namespace hetest
191 
192 namespace hetest {
193 namespace utils {
194 
196  virtual ~AllocatorBase() noexcept {}
197  virtual void* allocate(size_t bytes_count) = 0;
198  virtual void deallocate(void* p, size_t n) = 0;
199 };
200 
201 template <class AllocatorImpl>
203  // override interface & delegate implementation to AllocatorImpl
204  void* allocate(size_t bytes_count) override {
205  return static_cast<AllocatorImpl*>(this)->allocate_impl(bytes_count);
206  }
207  void deallocate(void* p, size_t n) override {
208  static_cast<AllocatorImpl*>(this)->deallocate_impl(p, n);
209  }
210 
211 private:
212  // in case of AllocatorImpl doesn't provide implementations use default
213  // behavior: break compilation with error
214  void* allocate_impl(size_t bytes_count) {
215  (void)bytes_count;
216  fail_message<0>();
217  return nullptr;
218  }
219  void deallocate_impl(void* p, size_t n) {
220  (void)p;
221  (void)n;
222  fail_message<1>();
223  }
224 
225  // pretty compilation error printing
226  template <int error_code>
227  static constexpr int fail_message() {
228  static_assert(
229  !(error_code == 0),
230  "Using 'AllocatorAdapter`as interface requires to implement "
231  "'::allocate_impl` method");
232  static_assert(
233  !(error_code == 1),
234  "Using 'AllocatorAdapter`as interface requires to implement "
235  "'::deallocate_impl` method");
236  return 0;
237  }
238 };
239 } // namespace utils
240 } // namespace hetest
241 
242 namespace hetest {
243 namespace utils {
244 
245 namespace details {
247  void* allocate(size_t bytes_count) final {
248  return std::malloc(bytes_count);
249  }
250 
251  void deallocate(void* p, size_t n) final {
252  (void)n;
253  std::free(p);
254  }
255 };
256 
258  explicit CustomAllocStrategy(std::shared_ptr<AllocatorBase> impl)
259  : p_impl(impl) {
260  if (!impl) {
261  throw std::runtime_error(
262  "Cannot create 'CustomAllocStrategy' without `impl`");
263  }
264  }
265 
266  void* allocate_memory(size_t bytes_count) {
267  return p_impl->allocate(bytes_count);
268  }
269 
270  void deallocate_memory(void* p, size_t n) { p_impl->deallocate(p, n); }
271 
272 private:
273  std::shared_ptr<AllocatorBase> p_impl;
274 };
275 } // namespace details
276 
277 using AllocatorStrategyPtr = std::shared_ptr<AllocatorBase>;
279 
280 template <typename T, uint64_t Alignment>
282 public:
283  template <typename, uint64_t>
284  friend class AlignedAllocator;
285 
286  using value_type = T;
287 
288  explicit AlignedAllocator(AllocatorStrategyPtr strategy = nullptr) noexcept
289  : m_alloc_impl((strategy != nullptr) ? strategy : mallocStrategy) {}
290 
292  : m_alloc_impl(src.m_alloc_impl) {}
293 
294  template <typename U>
296  : m_alloc_impl(src.m_alloc_impl) {}
297 
299 
300  template <typename U>
301  struct rebind {
303  };
304 
305  bool operator==(const AlignedAllocator&) { return true; }
306 
307  bool operator!=(const AlignedAllocator&) { return false; }
308 
309  T* allocate(size_t n) {
310  if (!IsPowerOfTwo(Alignment)) {
311  return nullptr;
312  }
313  // Allocate enough space to ensure the alignment can be satisfied
314  size_t buffer_size = sizeof(T) * n + Alignment;
315  // Additionally, allocate a prefix to store the memory location of the
316  // unaligned buffer
317  size_t alloc_size = buffer_size + sizeof(void*);
318  void* buffer = m_alloc_impl->allocate(alloc_size);
319  if (!buffer) {
320  return nullptr;
321  }
322 
323  // Reserve first location for pointer to originally-allocated space
324  void* aligned_buffer = static_cast<char*>(buffer) + sizeof(void*);
325  // std::align(Alignment, sizeof(T) * n, aligned_buffer, buffer_size);
326  if (!aligned_buffer) {
327  return nullptr;
328  }
329 
330  // Store allocated buffer address at aligned_buffer - sizeof(void*).
331  void* store_buffer_addr =
332  static_cast<char*>(aligned_buffer) - sizeof(void*);
333  *(static_cast<void**>(store_buffer_addr)) = buffer;
334 
335  return static_cast<T*>(aligned_buffer);
336  }
337 
338  void deallocate(T* p, size_t n) {
339  if (!p) {
340  return;
341  }
342  void* store_buffer_addr = (reinterpret_cast<char*>(p) - sizeof(void*));
343  void* free_address = *(static_cast<void**>(store_buffer_addr));
344  m_alloc_impl->deallocate(free_address, n);
345  }
346 
347 private:
348  AllocatorStrategyPtr m_alloc_impl;
349 };
350 
351 template <typename T>
352 using AlignedVector64 = std::vector<T, AlignedAllocator<T, 64> >;
353 
354 } // namespace utils
355 } // namespace hetest
~AlignedAllocator()
Definition: utils-test.hpp:298
Definition: utils-test.hpp:202
uint64_t DivideUInt128UInt64Lo(uint64_t x1, uint64_t x0, uint64_t y)
Definition: utils-test.hpp:108
void * allocate_memory(size_t bytes_count)
Definition: utils-test.hpp:266
virtual void deallocate(void *p, size_t n)=0
uint64_t Log2(uint64_t x)
Definition: utils-test.hpp:176
bool Compare(CMPINT cmp, uint64_t lhs, uint64_t rhs)
Definition: utils-test.hpp:144
uint64_t BarrettReduce128(uint64_t input_hi, uint64_t input_lo, uint64_t modulus)
Definition: utils-test.hpp:97
uint128_t MultiplyUInt64(uint64_t x, uint64_t y)
Definition: utils-test.hpp:93
void deallocate(void *p, size_t n) final
Definition: utils-test.hpp:251
void deallocate_memory(void *p, size_t n)
Definition: utils-test.hpp:270
CMPINT Not(CMPINT cmp)
Returns the logical negation of a binary operation.
Definition: utils-test.hpp:39
std::vector< T, AlignedAllocator< T, 64 > > AlignedVector64
Definition: utils-test.hpp:352
CustomAllocStrategy(std::shared_ptr< AllocatorBase > impl)
Definition: utils-test.hpp:258
uint64_t MSB(uint64_t input)
Definition: utils-test.hpp:134
CMPINT
Represents binary operations between two boolean values.
Definition: utils-test.hpp:26
AlignedAllocator(const AlignedAllocator< U, Alignment > &src)
Definition: utils-test.hpp:295
bool IsPowerOfTwo(uint64_t num)
Definition: utils-test.hpp:173
__extension__ typedef unsigned __int128 uint128_t
Definition: utils-test.hpp:16
AlignedAllocator(AllocatorStrategyPtr strategy=nullptr) noexcept
Definition: utils-test.hpp:288
uint64_t MultiplyUInt64Hi(uint64_t x, uint64_t y)
Definition: utils-test.hpp:128
Not less than or equal.
Definition: utils-test.hpp:195
Definition: utils-test.hpp:301
#define UTILS_CHECK(cond, expr)
Definition: utils-test.hpp:81
void * allocate(size_t bytes_count) final
Definition: utils-test.hpp:247
__extension__ typedef __int128 int128_t
Definition: utils-test.hpp:15
uint64_t MaximumValue(uint64_t bits)
Definition: utils-test.hpp:182
AlignedAllocator(const AlignedAllocator &src)
Definition: utils-test.hpp:291
void deallocate(T *p, size_t n)
Definition: utils-test.hpp:338
bool operator==(const AlignedAllocator &)
Definition: utils-test.hpp:305
bool operator!=(const AlignedAllocator &)
Definition: utils-test.hpp:307
virtual ~AllocatorBase() noexcept
Definition: utils-test.hpp:196
void deallocate(void *p, size_t n) override
Definition: utils-test.hpp:207
Definition: utils-test.hpp:246
virtual void * allocate(size_t bytes_count)=0
void * allocate(size_t bytes_count) override
Definition: utils-test.hpp:204
Less than or equal.
Definition: utils-test.hpp:281
T value_type
Definition: utils-test.hpp:286
std::shared_ptr< AllocatorBase > AllocatorStrategyPtr
Definition: utils-test.hpp:277
Definition: utils-test.hpp:257
T * allocate(size_t n)
Definition: utils-test.hpp:309
AllocatorStrategyPtr mallocStrategy
Definition: ntt.cpp:255