Intel HE Acceleration Library for FPGAs
Intel Homomorphic Encryption Acceleration Library for FPGAs, accelerating the modular arithmetic operations used in homomorphic encryption on Intel FPGAs.
 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  AlignedAllocator& operator=(const AlignedAllocator& src) = delete;
295 
296  template <typename U>
298  : m_alloc_impl(src.m_alloc_impl) {}
299 
301 
302  template <typename U>
303  struct rebind {
305  };
306 
307  bool operator==(const AlignedAllocator&) { return true; }
308 
309  bool operator!=(const AlignedAllocator&) { return false; }
310 
311  T* allocate(size_t n) {
312  if (!IsPowerOfTwo(Alignment)) {
313  return nullptr;
314  }
315  // Allocate enough space to ensure the alignment can be satisfied
316  size_t buffer_size = sizeof(T) * n + Alignment;
317  // Additionally, allocate a prefix to store the memory location of the
318  // unaligned buffer
319  size_t alloc_size = buffer_size + sizeof(void*);
320  void* buffer = m_alloc_impl->allocate(alloc_size);
321  if (!buffer) {
322  return nullptr;
323  }
324 
325  // Reserve first location for pointer to originally-allocated space
326  void* aligned_buffer = static_cast<char*>(buffer) + sizeof(void*);
327  // std::align(Alignment, sizeof(T) * n, aligned_buffer, buffer_size);
328  if (!aligned_buffer) {
329  return nullptr;
330  }
331 
332  // Store allocated buffer address at aligned_buffer - sizeof(void*).
333  void* store_buffer_addr =
334  static_cast<char*>(aligned_buffer) - sizeof(void*);
335  *(static_cast<void**>(store_buffer_addr)) = buffer;
336 
337  return static_cast<T*>(aligned_buffer);
338  }
339 
340  void deallocate(T* p, size_t n) {
341  if (!p) {
342  return;
343  }
344  void* store_buffer_addr = (reinterpret_cast<char*>(p) - sizeof(void*));
345  void* free_address = *(static_cast<void**>(store_buffer_addr));
346  m_alloc_impl->deallocate(free_address, n);
347  }
348 
349 private:
350  AllocatorStrategyPtr m_alloc_impl;
351 };
352 
353 template <typename T>
354 using AlignedVector64 = std::vector<T, AlignedAllocator<T, 64> >;
355 
356 } // namespace utils
357 } // namespace hetest
~AlignedAllocator()
Definition: utils-test.hpp:300
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
AlignedAllocator & operator=(const AlignedAllocator &src)=delete
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:354
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:297
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:303
#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:340
bool operator==(const AlignedAllocator &)
Definition: utils-test.hpp:307
bool operator!=(const AlignedAllocator &)
Definition: utils-test.hpp:309
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:311
AllocatorStrategyPtr mallocStrategy
Definition: ntt.cpp:255