HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
boolean_function.cpp
Go to the documentation of this file.
2 
8 
9 #include <algorithm>
10 #include <bitset>
11 #include <boost/spirit/home/x3.hpp>
12 #include <chrono>
13 #include <map>
14 #include <iomanip>
15 
16 namespace hal
17 {
18  template<>
19  std::map<BooleanFunction::Value, std::string> EnumStrings<BooleanFunction::Value>::data = {{BooleanFunction::Value::ZERO, "0"},
20  {BooleanFunction::Value::ONE, "1"},
21  {BooleanFunction::Value::X, "X"},
22  {BooleanFunction::Value::Z, "Z"}};
23 
25  {
26  switch (v)
27  {
28  case ZERO:
29  return std::string("0");
30  case ONE:
31  return std::string("1");
32  case X:
33  return std::string("X");
34  case Z:
35  return std::string("Z");
36  }
37 
38  return std::string("X");
39  }
40 
41  namespace
42  {
43  static std::vector<char> char_map = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
44  // namespace
45 
46  static Result<std::string> to_bin(const std::vector<BooleanFunction::Value>& value)
47  {
48  if (value.size() == 0)
49  {
50  return ERR("could not convert bit-vector to binary string: bit-vector is empty");
51  }
52 
53  std::string res = "";
54  res.reserve(value.size());
55 
56  for (auto v : value)
57  {
58  res += enum_to_string<BooleanFunction::Value>(v);
59  }
60 
61  return OK(res);
62  }
63 
64  static Result<std::string> to_oct(const std::vector<BooleanFunction::Value>& value)
65  {
66  int bitsize = value.size();
67  if (bitsize == 0)
68  {
69  return ERR("could not convert bit-vector to octal string: bit-vector is empty");
70  }
71 
72  u8 first_bits = bitsize % 3;
73 
74  u8 index = 0;
75  u8 mask = 0;
76 
77  u8 v1, v2, v3;
78 
79  // result string prep
80  std::string res = "";
81  res.reserve((bitsize + 2) / 3);
82 
83  // deal with 0-3 leading bits
84  for (u8 i = 0; i < first_bits; i++)
85  {
86  v1 = value.at(i);
87  index = (index << 1) | v1;
88  mask |= v1;
89  }
90  mask = -((mask >> 1) & 0x1);
91  if (first_bits)
92  {
93  res += (char_map[index] & ~mask) | ('X' & mask);
94  }
95 
96  // deal with 4-bit blocks (left to right)
97  for (int i = bitsize % 3; i < bitsize; i += 3)
98  {
99  v1 = value[i];
100  v2 = value[i + 1];
101  v3 = value[i + 2];
102 
103  index = (v1 << 2) | (v2 << 1) | v3; // cannot exceed char_map range as index always < 16, no further check required
104  mask = -(((v1 | v2 | v3) >> 1) & 0x1);
105 
106  res += (char_map[index] & ~mask) | ('X' & mask);
107  }
108  return OK(res);
109  }
110 
111  static Result<std::string> to_dec(const std::vector<BooleanFunction::Value>& value)
112  {
113  int bitsize = value.size();
114  if (bitsize == 0)
115  {
116  return ERR("could not convert bit-vector to decimal string: bit-vector is empty");
117  }
118 
119  if (bitsize > 64)
120  {
121  return ERR("could not convert bit-vector to decimal string: bit-vector has length " + std::to_string(bitsize) + ", but only up to 64 bits are supported for decimal conversion");
122  }
123 
124  u64 tmp = 0;
125  u8 x_flag = 0;
126 
127  for (auto it = value.rbegin(); it != value.rend(); it++)
128  {
129  x_flag |= *it >> 1;
130  tmp <<= 1;
131  tmp |= *it;
132  }
133 
134  if (x_flag)
135  {
136  return OK(std::string("X"));
137  }
138  return OK(std::to_string(tmp));
139  }
140 
141  static Result<std::string> to_hex(const std::vector<BooleanFunction::Value>& value)
142  {
143  int bitsize = value.size();
144  if (bitsize == 0)
145  {
146  return ERR("could not convert bit-vector to hexadecimal string: bit-vector is empty");
147  }
148 
149  u8 first_bits = bitsize & 0x3;
150 
151  u8 index = 0;
152  u8 mask = 0;
153 
154  u8 v1, v2, v3, v4;
155 
156  // result string prep
157  std::string res = "";
158  res.reserve((bitsize + 3) / 4);
159 
160  // deal with 0-3 leading bits
161  for (u8 i = 0; i < first_bits; i++)
162  {
163  v1 = value.at(i);
164  index = (index << 1) | v1;
165  mask |= v1;
166  }
167  mask = -((mask >> 1) & 0x1);
168  if (first_bits)
169  {
170  res += (char_map[index] & ~mask) | ('X' & mask);
171  }
172 
173  // deal with 4-bit blocks (left to right)
174  for (int i = bitsize & 0x3; i < bitsize; i += 4)
175  {
176  v1 = value[i];
177  v2 = value[i + 1];
178  v3 = value[i + 2];
179  v4 = value[i + 3];
180 
181  index = ((v1 << 3) | (v2 << 2) | (v3 << 1) | v4) & 0xF;
182  mask = -(((v1 | v2 | v3 | v4) >> 1) & 0x1);
183 
184  res += (char_map[index] & ~mask) | ('X' & mask);
185  }
186 
187  return OK(res);
188  }
189  } // namespace
190 
191  Result<std::string> BooleanFunction::to_string(const std::vector<BooleanFunction::Value>& value, u8 base)
192  {
193  switch (base)
194  {
195  case 2:
196  return to_bin(value);
197  case 8:
198  return to_oct(value);
199  case 10:
200  return to_dec(value);
201  case 16:
202  return to_hex(value);
203  default:
204  return ERR("could not convert bit-vector to string: invalid value '" + std::to_string(base) + "' given for base");
205  }
206  }
207 
208  Result<u64> BooleanFunction::to_u64(const std::vector<BooleanFunction::Value>& value)
209  {
210  if (value.size() > 64)
211  {
212  return ERR("cannot translate vector of values to u64 numeral: can only support vectors up to 64 bits and got vector of size " + std::to_string(value.size()) + ".");
213  }
214 
215  u64 val = 0;
216  for (auto it = value.rbegin(); it != value.rend(); it++)
217  {
218  if ((*it != BooleanFunction::Value::ZERO) && (*it != BooleanFunction::Value::ONE))
219  {
220  return ERR("cannot translate vector of values to u64 numeral: found value other than ZERO or ONE: " + BooleanFunction::to_string(*it) + ".");
221  }
222 
223  val <<= 1;
224  val |= *it;
225  }
226 
227  return OK(val);
228  }
229 
230  std::ostream& operator<<(std::ostream& os, BooleanFunction::Value v)
231  {
232  return os << BooleanFunction::to_string(v);
233  }
234 
236  {
237  }
238 
239  Result<BooleanFunction> BooleanFunction::build(std::vector<BooleanFunction::Node>&& nodes)
240  {
241  if (auto res = BooleanFunction::validate(BooleanFunction(std::move(nodes))); res.is_error())
242  {
243  return ERR_APPEND(res.get_error(), "could not build Boolean function from vector of nodes: failed to validate Boolean function");
244  }
245  else
246  {
247  return res;
248  }
249  }
250 
252  {
254  }
255 
257  {
258  return BooleanFunction(Node::Constant({value}));
259  }
260 
261  BooleanFunction BooleanFunction::Const(const std::vector<BooleanFunction::Value>& values)
262  {
263  return BooleanFunction(Node::Constant(values));
264  }
265 
267  {
268  auto values = std::vector<BooleanFunction::Value>();
269  values.reserve(size);
270  for (auto i = 0; i < size; i++)
271  {
272  values.emplace_back(((value >> i) & 1) ? BooleanFunction::Value::ONE : BooleanFunction::Value::ZERO);
273  }
274 
275  return BooleanFunction::Const(values);
276  }
277 
279  {
281  }
282 
284  {
285  if ((p0.size() != p1.size()) || (p0.size() != size))
286  {
287  return ERR("could not join Boolean functions using AND operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
288  + ", size = " + std::to_string(size) + ")");
289  }
290 
291  return OK(BooleanFunction(Node::Operation(NodeType::And, size), std::move(p0), std::move(p1)));
292  }
293 
295  {
296  if ((p0.size() != p1.size()) || (p0.size() != size))
297  {
298  return ERR("could not join Boolean functions using OR operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
299  + ", size = " + std::to_string(size) + ").");
300  }
301 
302  return OK(BooleanFunction(Node::Operation(NodeType::Or, size), std::move(p0), std::move(p1)));
303  }
304 
306  {
307  if (p0.size() != size)
308  {
309  return ERR("could not invert Boolean function using NOT operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", size = " + std::to_string(size) + ").");
310  }
311 
312  return OK(BooleanFunction(Node::Operation(NodeType::Not, size), std::move(p0)));
313  }
314 
316  {
317  if ((p0.size() != p1.size()) || (p0.size() != size))
318  {
319  return ERR("could not join Boolean functions using XOR operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
320  + ", size = " + std::to_string(size) + ").");
321  }
322 
323  return OK(BooleanFunction(Node::Operation(NodeType::Xor, size), std::move(p0), std::move(p1)));
324  }
325 
327  {
328  if ((p0.size() != p1.size()) || (p0.size() != size))
329  {
330  return ERR("could not join Boolean functions using ADD operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
331  + ", size = " + std::to_string(size) + ").");
332  }
333 
334  return OK(BooleanFunction(Node::Operation(NodeType::Add, size), std::move(p0), std::move(p1)));
335  }
336 
338  {
339  if ((p0.size() != p1.size()) || (p0.size() != size))
340  {
341  return ERR("could not join Boolean functions using SUB operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
342  + ", size = " + std::to_string(size) + ").");
343  }
344 
345  return OK(BooleanFunction(Node::Operation(NodeType::Sub, size), std::move(p0), std::move(p1)));
346  }
347 
349  {
350  if ((p0.size() != p1.size()) || (p0.size() != size))
351  {
352  return ERR("could not join Boolean functions using MUL operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
353  + ", size = " + std::to_string(size) + ").");
354  }
355 
356  return OK(BooleanFunction(Node::Operation(NodeType::Mul, size), std::move(p0), std::move(p1)));
357  }
358 
360  {
361  if ((p0.size() != p1.size()) || (p0.size() != size))
362  {
363  return ERR("could not join Boolean functions using SDIV operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
364  + ", size = " + std::to_string(size) + ").");
365  }
366 
367  return OK(BooleanFunction(Node::Operation(NodeType::Sdiv, size), std::move(p0), std::move(p1)));
368  }
369 
371  {
372  if ((p0.size() != p1.size()) || (p0.size() != size))
373  {
374  return ERR("could not join Boolean functions using UDIV operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
375  + ", size = " + std::to_string(size) + ").");
376  }
377 
378  return OK(BooleanFunction(Node::Operation(NodeType::Udiv, size), std::move(p0), std::move(p1)));
379  }
380 
382  {
383  if ((p0.size() != p1.size()) || (p0.size() != size))
384  {
385  return ERR("could not join Boolean functions using SREM operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
386  + ", size = " + std::to_string(size) + ").");
387  }
388 
389  return OK(BooleanFunction(Node::Operation(NodeType::Srem, size), std::move(p0), std::move(p1)));
390  }
391 
393  {
394  if ((p0.size() != p1.size()) || (p0.size() != size))
395  {
396  return ERR("could not join Boolean functions using UREM operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size())
397  + ", size = " + std::to_string(size) + ").");
398  }
399 
400  return OK(BooleanFunction(Node::Operation(NodeType::Urem, size), std::move(p0), std::move(p1)));
401  }
402 
404  {
405  if (!p1.is_index() || !p2.is_index())
406  {
407  return ERR("could not apply slice operation: function types do not match (p1 and p2 must be of type 'BooleanFunction::Index')");
408  }
409  if ((p0.size() != p1.size()) || (p1.size() != p2.size()))
410  {
411  return ERR("could not apply slice operation: bit-sizes do not match (p0 = " + std::to_string(p0.size()) + ", p1 = " + std::to_string(p1.size()) + ", p2 = " + std::to_string(p2.size())
412  + " - sizes must be equal)");
413  }
414 
415  auto start = p1.get_index_value().get();
416  auto end = p2.get_index_value().get();
417  if ((start > end) || (start >= p0.size()) || (end >= p0.size()) || (end - start + 1) != size)
418  {
419  return ERR("could not apply SLICE operation: bit indices are not valid, p1 must be larger or equal than p1 and smaller than p0 (p0 = " + std::to_string(p0.size())
420  + ", p1 = " + std::to_string(start) + ", p2 = " + std::to_string(end) + ")");
421  }
422 
423  return OK(BooleanFunction(Node::Operation(NodeType::Slice, size), std::move(p0), std::move(p1), std::move(p2)));
424  }
425 
427  {
428  if ((p0.size() + p1.size()) != size)
429  {
430  return ERR("could not apply CONCAT operation: function input widths do not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
431  + "-bit, size = " + std::to_string(size) + ").");
432  }
433 
434  return OK(BooleanFunction(Node::Operation(NodeType::Concat, size), std::move(p0), std::move(p1)));
435  }
436 
438  {
439  if (p0.size() > size || p1.size() != size)
440  {
441  return ERR("could not apply ZEXT operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
442  + "-bit, size = " + std::to_string(size) + ").");
443  }
444 
445  if (!p1.has_index_value(size))
446  {
447  return ERR("could not apply ZEXT operation: p1 does not encode size (p1 = " + p1.to_string() + ", size = " + std::to_string(size) + ").");
448  }
449 
450  return OK(BooleanFunction(Node::Operation(NodeType::Zext, size), std::move(p0), std::move(p1)));
451  }
452 
454  {
455  if (p0.size() > size || p1.size() != size)
456  {
457  return ERR("could not apply SEXT operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
458  + "-bit, size = " + std::to_string(size) + ").");
459  }
460 
461  if (!p1.has_index_value(size))
462  {
463  return ERR("could not apply SEXT operation: p1 does not encode size (p1 = " + p1.to_string() + ", size = " + std::to_string(size) + ").");
464  }
465 
466  return OK(BooleanFunction(Node::Operation(NodeType::Sext, size), std::move(p0), std::move(p1)));
467  }
468 
470  {
471  if (p0.size() != size || p1.size() != size)
472  {
473  return ERR("could not apply SHL operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
474  + "-bit, size = " + std::to_string(size) + ").");
475  }
476 
477  if (!p1.is_index())
478  {
479  return ERR("could not apply SHL operation: p1 is not an index.");
480  }
481 
482  return OK(BooleanFunction(Node::Operation(NodeType::Shl, size), std::move(p0), std::move(p1)));
483  }
484 
486  {
487  if (p0.size() != size || p1.size() != size)
488  {
489  return ERR("could not apply LSHR operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
490  + "-bit, size = " + std::to_string(size) + ").");
491  }
492 
493  if (!p1.is_index())
494  {
495  return ERR("could not apply LSHR operation: p1 is not an index.");
496  }
497 
498  return OK(BooleanFunction(Node::Operation(NodeType::Lshr, size), std::move(p0), std::move(p1)));
499  }
500 
502  {
503  if (p0.size() != size || p1.size() != size)
504  {
505  return ERR("could not apply ASHR operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
506  + "-bit, size = " + std::to_string(size) + ").");
507  }
508 
509  if (!p1.is_index())
510  {
511  return ERR("could not apply ASHR operation: p1 is not an index.");
512  }
513 
514  return OK(BooleanFunction(Node::Operation(NodeType::Ashr, size), std::move(p0), std::move(p1)));
515  }
516 
518  {
519  if (p0.size() != size || p1.size() != size)
520  {
521  return ERR("could not apply ROL operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
522  + "-bit, size = " + std::to_string(size) + ").");
523  }
524 
525  if (!p1.is_index())
526  {
527  return ERR("could not apply ROL operation: p1 is not an index.");
528  }
529 
530  return OK(BooleanFunction(Node::Operation(NodeType::Rol, size), std::move(p0), std::move(p1)));
531  }
532 
534  {
535  if (p0.size() != size || p1.size() != size)
536  {
537  return ERR("could not apply ROR operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
538  + "-bit, size = " + std::to_string(size) + ").");
539  }
540 
541  if (!p1.is_index())
542  {
543  return ERR("could not apply ROR operation: p1 is not an index.");
544  }
545 
546  return OK(BooleanFunction(Node::Operation(NodeType::Ror, size), std::move(p0), std::move(p1)));
547  }
548 
550  {
551  if (p0.size() != p1.size() || size != 1)
552  {
553  return ERR("could not apply EQ operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
554  + "-bit, size = " + std::to_string(size) + ").");
555  }
556 
557  return OK(BooleanFunction(Node::Operation(NodeType::Eq, size), std::move(p0), std::move(p1)));
558  }
559 
561  {
562  if (p0.size() != p1.size() || size != 1)
563  {
564  return ERR("could not apply SLE operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
565  + "-bit, size = " + std::to_string(size) + ").");
566  }
567 
568  return OK(BooleanFunction(Node::Operation(NodeType::Sle, size), std::move(p0), std::move(p1)));
569  }
570 
572  {
573  if (p0.size() != p1.size() || size != 1)
574  {
575  return ERR("could not apply SLT operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
576  + "-bit, size = " + std::to_string(size) + ").");
577  }
578 
579  return OK(BooleanFunction(Node::Operation(NodeType::Slt, size), std::move(p0), std::move(p1)));
580  }
581 
583  {
584  if (p0.size() != p1.size() || size != 1)
585  {
586  return ERR("could not apply ULE operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
587  + "-bit, size = " + std::to_string(size) + ").");
588  }
589 
590  return OK(BooleanFunction(Node::Operation(NodeType::Ule, size), std::move(p0), std::move(p1)));
591  }
592 
594  {
595  if (p0.size() != p1.size() || size != 1)
596  {
597  return ERR("could not apply ULT operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
598  + "-bit, size = " + std::to_string(size) + ").");
599  }
600 
601  return OK(BooleanFunction(Node::Operation(NodeType::Ult, size), std::move(p0), std::move(p1)));
602  }
603 
605  {
606  if (p0.size() != 1 || p1.size() != size || p2.size() != size)
607  {
608  return ERR("could not apply ITE operation: function input width does not match (p0 = " + std::to_string(p0.size()) + "-bit, p1 = " + std::to_string(p1.size())
609  + "-bit, p2 = " + std::to_string(p2.size()) + "-bit, size = " + std::to_string(size) + ").");
610  }
611 
612  return OK(BooleanFunction(Node::Operation(NodeType::Ite, size), std::move(p0), std::move(p1), std::move(p2)));
613  }
614 
615  std::ostream& operator<<(std::ostream& os, const BooleanFunction& f)
616  {
617  return os << f.to_string();
618  }
619 
621  {
622  return BooleanFunction::And(this->clone(), other.clone(), this->size()).get();
623  }
624 
626  {
627  *this = BooleanFunction::And(this->clone(), other.clone(), this->size()).get();
628  return *this;
629  }
630 
632  {
633  return BooleanFunction::Not(this->clone(), this->size()).get();
634  }
635 
637  {
638  return BooleanFunction::Or(this->clone(), other.clone(), this->size()).get();
639  }
640 
642  {
643  *this = BooleanFunction::Or(this->clone(), other.clone(), this->size()).get();
644  return *this;
645  }
646 
648  {
649  return BooleanFunction::Xor(this->clone(), other.clone(), this->size()).get();
650  }
651 
653  {
654  *this = BooleanFunction::Xor(this->clone(), other.clone(), this->size()).get();
655  return *this;
656  }
657 
659  {
660  return BooleanFunction::Add(this->clone(), other.clone(), this->size()).get();
661  }
662 
664  {
665  *this = BooleanFunction::Add(this->clone(), other.clone(), this->size()).get();
666  return *this;
667  }
668 
670  {
671  return BooleanFunction::Sub(this->clone(), other.clone(), this->size()).get();
672  }
673 
675  {
676  *this = BooleanFunction::Sub(this->clone(), other.clone(), this->size()).get();
677  return *this;
678  }
679 
681  {
682  return BooleanFunction::Mul(this->clone(), other.clone(), this->size()).get();
683  }
684 
686  {
687  *this = BooleanFunction::Mul(this->clone(), other.clone(), this->size()).get();
688  return *this;
689  }
690 
692  {
693  if (this->m_nodes.size() != other.m_nodes.size())
694  {
695  return false;
696  }
697 
698  for (auto i = 0ul; i < this->m_nodes.size(); i++)
699  {
700  if (this->m_nodes[i] != other.m_nodes[i])
701  {
702  return false;
703  }
704  }
705  return true;
706  }
707 
709  {
710  return !(*this == other);
711  }
712 
714  {
715  if (this->m_nodes.size() < other.m_nodes.size())
716  {
717  return true;
718  }
719  if (this->m_nodes.size() > other.m_nodes.size())
720  {
721  return false;
722  }
723 
724  // compare the nodes directly instead of their string representation, this operator is on the hot path
725  // of every symbolic state lookup and formatting two strings per comparison dominated the evaluation
726  return std::lexicographical_compare(this->m_nodes.begin(), this->m_nodes.end(), other.m_nodes.begin(), other.m_nodes.end());
727  }
728 
730  {
731  return this->m_nodes.empty();
732  }
733 
735  {
736  auto function = BooleanFunction();
737  function.m_nodes.reserve(this->m_nodes.size());
738 
739  for (const auto& node : this->m_nodes)
740  {
741  function.m_nodes.emplace_back(node);
742  }
743 
744  return function;
745  }
746 
748  {
749  return this->get_top_level_node().size;
750  }
751 
753  {
754  return (this->is_empty()) ? false : this->get_top_level_node().is(type);
755  }
756 
758  {
759  return (this->is_empty()) ? false : this->get_top_level_node().is_variable();
760  }
761 
762  bool BooleanFunction::has_variable_name(const std::string& variable_name) const
763  {
764  return (this->is_empty()) ? false : this->get_top_level_node().has_variable_name(variable_name);
765  }
766 
768  {
769  if (this->is_empty())
770  {
771  return ERR("Boolean function is empty");
772  }
773 
774  return this->get_top_level_node().get_variable_name();
775  }
776 
778  {
779  return (this->is_empty()) ? false : this->get_top_level_node().is_constant();
780  }
781 
782  bool BooleanFunction::has_constant_value(const std::vector<Value>& value) const
783  {
784  return (this->is_empty()) ? false : this->get_top_level_node().has_constant_value(value);
785  }
786 
788  {
789  return (this->is_empty()) ? false : this->get_top_level_node().has_constant_value(value);
790  }
791 
793  {
794  if (this->is_empty())
795  {
796  return ERR("Boolean function is empty");
797  }
798 
799  return this->get_top_level_node().get_constant_value();
800  }
801 
803  {
804  if (this->is_empty())
805  {
806  return ERR("Boolean function is empty");
807  }
808 
810  }
811 
813  {
814  return (this->is_empty()) ? false : this->get_top_level_node().is_index();
815  }
816 
818  {
819  return (this->is_empty()) ? false : this->get_top_level_node().has_index_value(value);
820  }
821 
823  {
824  if (this->is_empty())
825  {
826  return ERR("Boolean function is empty");
827  }
828 
829  return this->get_top_level_node().get_index_value();
830  }
831 
833  {
834  return this->m_nodes.back();
835  }
836 
838  {
839  return this->m_nodes.size();
840  }
841 
842  const std::vector<BooleanFunction::Node>& BooleanFunction::get_nodes() const
843  {
844  return this->m_nodes;
845  }
846 
847  std::vector<BooleanFunction> BooleanFunction::get_parameters() const
848  {
855 
856  auto coverage = this->compute_node_coverage();
857  switch (this->get_top_level_node().get_arity())
858  {
859  case 0: {
860  return {};
861  }
862  case 1: {
863  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.end() - 1}))};
864  }
865  case 2: {
866  auto index = this->length() - coverage[this->length() - 2] - 1;
867 
868  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.begin() + index})),
869  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index, this->m_nodes.end() - 1}))};
870  }
871  case 3: {
872  auto index0 = this->length() - coverage[this->length() - 3] - coverage[this->length() - 2] - 1;
873  auto index1 = this->length() - coverage[this->length() - 2] - 1;
874 
875  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.begin() + index0})),
876  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index0, this->m_nodes.begin() + index1})),
877  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index1, this->m_nodes.end() - 1}))};
878  }
879 
880  default:
881  assert(false && "not implemented reached.");
882  }
883 
884  return {};
885  }
886 
887  std::set<std::string> BooleanFunction::get_variable_names() const
888  {
889  auto variable_names = std::set<std::string>();
890  for (const auto& node : this->m_nodes)
891  {
892  if (node.is_variable())
893  {
894  variable_names.insert(node.variable);
895  }
896  }
897  return variable_names;
898  }
899 
900  Result<std::string> BooleanFunction::default_printer(const BooleanFunction::Node& node, std::vector<std::string>&& operands)
901  {
902  if (node.get_arity() != operands.size())
903  {
904  return ERR("could not print Boolean function: node arity of " + std::to_string(node.get_arity()) + " does not match number of operands of " + std::to_string(operands.size()));
905  }
906 
907  switch (node.type)
908  {
912  return OK(node.to_string());
913 
915  return OK("(" + operands[0] + " & " + operands[1] + ")");
917  return OK("(! " + operands[0] + ")");
919  return OK("(" + operands[0] + " | " + operands[1] + ")");
921  return OK("(" + operands[0] + " ^ " + operands[1] + ")");
922 
924  return OK("(" + operands[0] + " + " + operands[1] + ")");
926  return OK("(" + operands[0] + " - " + operands[1] + ")");
928  return OK("(" + operands[0] + " * " + operands[1] + ")");
930  return OK("(" + operands[0] + " /s " + operands[1] + ")");
932  return OK("(" + operands[0] + " / " + operands[1] + ")");
934  return OK("(" + operands[0] + " \%s " + operands[1] + ")");
936  return OK("(" + operands[0] + " \% " + operands[1] + ")");
937 
939  return OK("(" + operands[0] + " ++ " + operands[1] + ")");
941  return OK("Slice(" + operands[0] + ", " + operands[1] + ", " + operands[2] + ")");
943  return OK("Zext(" + operands[0] + ", " + operands[1] + ")");
945  return OK("Sext(" + operands[0] + ", " + operands[1] + ")");
946 
948  return OK("(" + operands[0] + " << " + operands[1] + ")");
950  return OK("(" + operands[0] + " >> " + operands[1] + ")");
952  return OK("(" + operands[0] + " >>a " + operands[1] + ")");
954  return OK("(" + operands[0] + " <<r " + operands[1] + ")");
956  return OK("(" + operands[0] + " >>r " + operands[1] + ")");
957 
959  return OK("(" + operands[0] + " == " + operands[1] + ")");
961  return OK("(" + operands[0] + " <s " + operands[1] + ")");
963  return OK("(" + operands[0] + " <=s " + operands[1] + ")");
965  return OK("(" + operands[0] + " < " + operands[1] + ")");
967  return OK("(" + operands[0] + " <= " + operands[1] + ")");
969  return OK("Ite(" + operands[0] + ", " + operands[1] + ", " + operands[2] + ")");
970 
971  default:
972  return ERR("could not print Boolean function: unsupported node type '" + std::to_string(node.type) + "'");
973  }
974  }
975 
976  Result<std::string> BooleanFunction::algebraic_printer(const BooleanFunction::Node& node, std::vector<std::string>&& operands)
977  {
978  if (node.get_arity() != operands.size())
979  {
980  return ERR("could not print Boolean function: node arity of " + std::to_string(node.get_arity()) + " does not match number of operands of " + std::to_string(operands.size()));
981  }
982 
983  switch (node.type)
984  {
987  return OK(node.to_string());
988 
990  return OK("CONST" + std::string(node.has_constant_value(0) ? "0" : "1"));
991 
993  return OK("(" + operands[0] + "*" + operands[1] + ")");
995  return OK("(! " + operands[0] + ")");
997  return OK("(" + operands[0] + "+" + operands[1] + ")");
998 
999  default:
1000  return ERR("could not print Boolean function: unsupported node type '" + std::to_string(node.type) + "'");
1001  }
1002  }
1003 
1004  std::string BooleanFunction::to_string(std::function<Result<std::string>(const BooleanFunction::Node& node, std::vector<std::string>&& operands)>&& printer) const
1005  {
1006  // (1) early termination in case the Boolean function is empty
1007  if (this->m_nodes.empty())
1008  {
1009  return "<empty>";
1010  }
1011 
1012  // (2) iterate the list of nodes and setup string from leafs to root
1013  std::vector<std::string> stack;
1014  for (const auto& node : this->m_nodes)
1015  {
1016  std::vector<std::string> operands;
1017 
1018  if (stack.size() < node.get_arity())
1019  {
1020  // log_error("netlist", "Cannot fetch {} nodes from the stack (= imbalanced stack with {} parts - {}).", node->get_arity(), stack.size(), this->to_string_in_reverse_polish_notation());
1021  return "";
1022  }
1023 
1024  std::move(stack.end() - static_cast<u64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1025  stack.erase(stack.end() - static_cast<u64>(node.get_arity()), stack.end());
1026 
1027  if (auto res = printer(node, std::move(operands)); res.is_ok())
1028  {
1029  stack.emplace_back(res.get());
1030  }
1031  else
1032  {
1033  log_error("netlist", "Cannot translate BooleanFunction::Node '{}' to a string: {}.", node.to_string(), res.get_error().get());
1034  return "";
1035  }
1036  }
1037 
1038  switch (stack.size())
1039  {
1040  case 1:
1041  return stack.back();
1042  default: {
1043  // log_error("netlist", "Cannot translate BooleanFunction (= imbalanced stack with {} remaining parts).", stack.size());
1044  return "";
1045  }
1046  }
1047  }
1048 
1050  {
1053 
1054  static const std::vector<std::tuple<ParserType, std::function<Result<std::vector<Token>>(const std::string&)>>> parsers = {
1057  {ParserType::LibertyNoSpace, BooleanFunctionParser::parse_with_liberty_grammar}
1058  };
1059 
1060  for (const auto& [parser_type, parser] : parsers)
1061  {
1062  std::string sanitized_expression = expression;
1063  ParserType used_parser_type = parser_type;
1064 
1065  if (parser_type == ParserType::LibertyNoSpace)
1066  {
1067  sanitized_expression.erase(
1068  std::remove(sanitized_expression.begin(), sanitized_expression.end(), ' '),
1069  sanitized_expression.end()
1070  );
1071  used_parser_type = ParserType::Liberty;
1072  }
1073 
1074  auto tokens = parser(sanitized_expression);
1075  // (1) skip if parser cannot translate to tokens
1076  if (tokens.is_error())
1077  {
1078  continue;
1079  }
1080 
1081  // (2) skip if cannot translate to valid reverse-polish notation
1082  tokens = BooleanFunctionParser::reverse_polish_notation(tokens.get(), sanitized_expression, used_parser_type);
1083  if (tokens.is_error())
1084  {
1085  continue;
1086  }
1087  // (3) skip if reverse-polish notation tokens are no valid Boolean function
1088  auto function = BooleanFunctionParser::translate(tokens.get(), sanitized_expression);
1089  if (function.is_error())
1090  {
1091  continue;
1092  }
1093  return function;
1094  }
1095  return ERR("could not parse Boolean function from string: no parser available for '" + expression + "'");
1096  }
1097 
1099  {
1100  auto simplified = Simplification::local_simplification(*this).map<BooleanFunction>([](const auto& s) { return Simplification::abc_simplification(s); }).map<BooleanFunction>([](const auto& s) {
1102  });
1103 
1104  return (simplified.is_ok()) ? simplified.get() : this->clone();
1105  }
1106 
1108  {
1109  auto simplified = Simplification::local_simplification(*this);
1110 
1111  return (simplified.is_ok()) ? simplified.get() : this->clone();
1112  }
1113 
1114  BooleanFunction BooleanFunction::substitute(const std::string& old_variable_name, const std::string& new_variable_name) const
1115  {
1116  auto function = this->clone();
1117  for (auto i = 0u; i < this->m_nodes.size(); i++)
1118  {
1119  if (this->m_nodes[i].has_variable_name(old_variable_name))
1120  {
1121  function.m_nodes[i] = Node::Variable(new_variable_name, this->m_nodes[i].size);
1122  }
1123  }
1124 
1125  return function;
1126  }
1127 
1128  Result<BooleanFunction> BooleanFunction::substitute(const std::string& name, const BooleanFunction& replacement) const
1129  {
1130  // Helper function to substitute a variable with a Boolean function.
1131  auto substitute_variable = [](const auto& node, auto&& operands, auto var_name, auto repl) -> BooleanFunction {
1132  if (node.has_variable_name(var_name))
1133  {
1134  return repl.clone();
1135  }
1136  return BooleanFunction(node.clone(), std::move(operands));
1137  };
1138 
1139  std::vector<BooleanFunction> stack;
1140  for (const auto& node : this->m_nodes)
1141  {
1142  std::vector<BooleanFunction> operands;
1143  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1144  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1145 
1146  stack.emplace_back(substitute_variable(node, std::move(operands), name, replacement));
1147  }
1148 
1149  switch (stack.size())
1150  {
1151  case 1:
1152  return OK(stack.back());
1153  default:
1154  return ERR("could not replace variable '" + name + "' with Boolean function '" + replacement.to_string() + "': validation failed, the operations may be imbalanced");
1155  }
1156  }
1157 
1158  BooleanFunction BooleanFunction::substitute(const std::map<std::string, std::string>& substitutions) const
1159  {
1160  auto function = this->clone();
1161  for (auto i = 0u; i < this->m_nodes.size(); i++)
1162  {
1163  if (const auto var_name_res = this->m_nodes[i].get_variable_name(); var_name_res.is_ok())
1164  {
1165  if (const auto it = substitutions.find(var_name_res.get()); it != substitutions.end())
1166  {
1167  function.m_nodes[i] = Node::Variable(it->second, this->m_nodes[i].size);
1168  }
1169  }
1170  }
1171 
1172  return function;
1173  }
1174 
1175  Result<BooleanFunction> BooleanFunction::substitute(const std::map<std::string, BooleanFunction>& substitutions) const
1176  {
1177  // Helper function to find the replacement for a variable and substitute it with a Boolean function.
1178  //
1179  // node - Node.
1180  // operands - Operands of node.
1181  // returns the AST replacement.
1182  auto substitute_variable = [substitutions](const auto& node, auto&& operands) -> BooleanFunction {
1183  if (node.is_variable())
1184  {
1185  if (auto repl_it = substitutions.find(node.variable); repl_it != substitutions.end())
1186  {
1187  return repl_it->second.clone();
1188  }
1189  }
1190  return BooleanFunction(node.clone(), std::move(operands));
1191  };
1192 
1193  std::vector<BooleanFunction> stack;
1194  for (const auto& node : this->m_nodes)
1195  {
1196  std::vector<BooleanFunction> operands;
1197  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1198  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1199 
1200  stack.emplace_back(substitute_variable(node, std::move(operands)));
1201  }
1202 
1203  switch (stack.size())
1204  {
1205  case 1:
1206  return OK(stack.back());
1207  default:
1208  return ERR("could not carry out multiple substitutions: validation failed, the operations may be imbalanced");
1209  }
1210  }
1211 
1212  Result<BooleanFunction::Value> BooleanFunction::evaluate(const std::unordered_map<std::string, Value>& inputs) const
1213  {
1214  // (0) workaround to preserve the API functionality
1215  if (this->m_nodes.empty())
1216  {
1217  return OK(BooleanFunction::Value::X);
1218  }
1219 
1220  // (1) validate whether the input sizes match the boolean function
1221  if (this->size() != 1)
1222  {
1223  return ERR("could not evaluate Boolean function '" + this->to_string() + "': using single-bit evaluation on a Boolean function of size " + std::to_string(this->size()) + " is illegal");
1224  }
1225 
1226  // (2) translate the input to n-bit to use the generic function
1227  auto generic_inputs = std::unordered_map<std::string, std::vector<Value>>();
1228  for (const auto& [name, value] : inputs)
1229  {
1230  generic_inputs.emplace(name, std::vector<Value>({value}));
1231  }
1232 
1233  auto value = this->evaluate(generic_inputs);
1234  if (value.is_ok())
1235  {
1236  // TODO i find that this is incorrect behavior -> only because the variables are single bit does not mean the whole result is -> does not take concat into account
1237  return OK(value.get()[0]);
1238  }
1239 
1240  return ERR(value.get_error());
1241  }
1242 
1243  Result<std::vector<BooleanFunction::Value>> BooleanFunction::evaluate(const std::unordered_map<std::string, std::vector<Value>>& inputs) const
1244  {
1245  // (0) workaround to preserve the API functionality
1246  if (this->m_nodes.empty())
1247  {
1248  return OK(std::vector<BooleanFunction::Value>({BooleanFunction::Value::X}));
1249  }
1250 
1251  // (1) validate whether the input sizes match the boolean function.
1252  // Walk the nodes once and look up each variable, rather than comparing every input name against
1253  // every node, since compute_truth_table() calls this for each of its rows.
1254  for (const auto& node : this->m_nodes)
1255  {
1256  if (!node.is_variable())
1257  {
1258  continue;
1259  }
1260 
1261  if (const auto it = inputs.find(node.variable); it != inputs.end() && node.size != it->second.size())
1262  {
1263  return ERR("could not evaluate Boolean function '" + this->to_string() + "': as the size of vairbale " + node.variable + " with size " + std::to_string(node.size)
1264  + " does not match the size of the provided input (" + std::to_string(it->second.size()) + ")");
1265  }
1266  }
1267 
1268  // (2) initialize the symbolic state using the input variables
1269  auto symbolic_execution = SMT::SymbolicExecution();
1270  for (const auto& [name, value] : inputs)
1271  {
1272  symbolic_execution.state.set(BooleanFunction::Var(name, value.size()), BooleanFunction::Const(value));
1273  }
1274 
1275  // (3) analyze the evaluation result and check whether the result is a
1276  // constant boolean function
1277  auto result = symbolic_execution.evaluate(*this);
1278  if (result.is_ok())
1279  {
1280  if (auto value = result.get(); value.is_constant())
1281  {
1282  return OK(value.get_top_level_node().constant);
1283  }
1284  return OK(std::vector<BooleanFunction::Value>(this->size(), BooleanFunction::Value::X));
1285  }
1286  return ERR(result.get_error());
1287  }
1288 
1289  Result<std::vector<std::vector<BooleanFunction::Value>>> BooleanFunction::compute_truth_table(const std::vector<std::string>& ordered_variables, bool remove_unknown_variables) const
1290  {
1291  auto variable_names_in_function = this->get_variable_names();
1292 
1293  // (1) check that each variable is just a single bit, otherwise we do
1294  // not generate a truth-table
1295  for (const auto& node : this->m_nodes)
1296  {
1297  if (node.is_variable() && node.size != 1)
1298  {
1299  return ERR("could not compute truth table for Boolean function '" + this->to_string() + "': unable to generate a truth-table for Boolean function with variables of > 1-bit");
1300  }
1301  }
1302 
1303  // (2) select either parameter or the Boolean function variables
1304  auto variables = ordered_variables;
1305  if (variables.empty())
1306  {
1307  variables = std::vector<std::string>(variable_names_in_function.begin(), variable_names_in_function.end());
1308  }
1309 
1310  // (3) remove any unknown variables from the truth table
1311  if (remove_unknown_variables)
1312  {
1313  variables.erase(
1314  std::remove_if(variables.begin(), variables.end(), [&variable_names_in_function](const auto& s) { return variable_names_in_function.find(s) == variable_names_in_function.end(); }),
1315  variables.end());
1316  }
1317 
1318  // (4.1) check that the function is not empty, otherwise we return a
1319  // Boolean function with a truth-table with 'X' values
1320  if (this->m_nodes.empty())
1321  {
1322  return OK(std::vector<std::vector<Value>>(1, std::vector<Value>(1 << variables.size(), Value::X)));
1323  }
1324 
1325  // (4.2) safety-check in case the number of variables is too large to process
1326  if (variables.size() > 10)
1327  {
1328  return ERR("could not compute truth table for Boolean function '" + this->to_string() + "': unable to generate truth-table with more than 10 variables");
1329  }
1330 
1331  std::vector<std::vector<Value>> truth_table(this->size(), std::vector<Value>(1 << variables.size(), Value::ZERO));
1332 
1333  // (5) iterate the truth-table rows and set each column accordingly
1334  for (auto value = 0u; value < ((u32)1 << variables.size()); value++)
1335  {
1336  std::unordered_map<std::string, std::vector<Value>> input;
1337  auto tmp = value;
1338  for (const auto& variable : variables)
1339  {
1340  input[variable] = ((tmp & 1) == 0) ? std::vector<Value>({Value::ZERO}) : std::vector<Value>({Value::ONE});
1341  tmp >>= 1;
1342  }
1343  auto result = this->evaluate(input);
1344  if (result.is_error())
1345  {
1346  return ERR(result.get_error());
1347  }
1348  auto output = result.get();
1349  for (auto index = 0u; index < truth_table.size(); index++)
1350  {
1351  truth_table[index][value] = output[index];
1352  }
1353  }
1354 
1355  return OK(truth_table);
1356  }
1357 
1358  Result<std::string> BooleanFunction::get_truth_table_as_string(const std::vector<std::string>& ordered_inputs, std::string function_name, bool remove_unknown_inputs) const
1359  {
1360  std::vector<std::string> inputs;
1361  auto inputs_set = this->get_variable_names();
1362  if (ordered_inputs.empty())
1363  {
1364  inputs = std::vector<std::string>(inputs_set.begin(), inputs_set.end());
1365  }
1366  else
1367  {
1368  inputs = ordered_inputs;
1369  }
1370 
1371  if (remove_unknown_inputs)
1372  {
1373  inputs.erase(std::remove_if(inputs.begin(), inputs.end(), [&inputs_set](const auto& s) { return inputs_set.find(s) == inputs_set.end(); }), inputs.end());
1374  }
1375 
1376  const auto res = compute_truth_table(inputs, false);
1377  if (res.is_error())
1378  {
1379  return ERR_APPEND(res.get_error(), "could not print truth table for Boolean function '" + this->to_string() + "': unable to compute truth table");
1380  }
1381  const auto truth_table = res.get();
1382 
1383  std::stringstream str("");
1384 
1385  u32 num_inputs = inputs.size();
1386  u32 num_outputs = truth_table.size();
1387 
1388  // table headers
1389  std::vector<u32> in_widths;
1390  for (const auto& var : inputs)
1391  {
1392  in_widths.push_back(var.size());
1393  str << " " << var << " |";
1394  }
1395 
1396  std::vector<u32> out_widths;
1397  if (function_name.empty())
1398  {
1399  function_name = "O";
1400  }
1401  if (num_outputs == 1)
1402  {
1403  str << "| " << function_name << " ";
1404  out_widths.push_back(function_name.size());
1405  }
1406  else
1407  {
1408  for (u32 i = 0; i < num_outputs; i++)
1409  {
1410  std::string var = function_name + "(" + std::to_string(i) + ")";
1411  str << "| " << var << " ";
1412  out_widths.push_back(var.size());
1413  }
1414  }
1415  str << std::endl;
1416 
1417  // rule below headers
1418  for (u32 i = 0; i < num_inputs; i++)
1419  {
1420  str << std::setw(in_widths.at(i) + 3) << std::setfill('-') << "+";
1421  }
1422  for (u32 i = 0; i < num_outputs; i++)
1423  {
1424  str << "+" << std::setw(out_widths.at(i) + 2) << std::setfill('-') << "-";
1425  }
1426  str << std::endl;
1427 
1428  // table values
1429  for (u32 i = 0; i < (u32)(1 << num_inputs); i++)
1430  {
1431  for (u32 j = 0; j < num_inputs; j++)
1432  {
1433  str << " " << std::left << std::setw(in_widths.at(j)) << std::setfill(' ') << ((i >> j) & 1) << " |";
1434  }
1435 
1436  for (u32 k = 0; k < num_outputs; k++)
1437  {
1438  str << "| " << std::left << std::setw(out_widths.at(k)) << std::setfill(' ') << truth_table.at(k).at(i) << " ";
1439  }
1440  str << std::endl;
1441  }
1442  return OK(str.str());
1443  }
1444 
1445  z3::expr BooleanFunction::to_z3(z3::context& context, const std::map<std::string, z3::expr>& var2expr) const
1446  {
1447  // Helper function to reduce a abstract syntax subtree to z3 expressions
1448  //
1449  // node - Boolean function node.
1450  // p - Boolean function node parameters.
1451  // returns (1) status (true on success, false otherwise),
1452  // (2) SMT-LIB string representation of node and operands.
1453  auto reduce_to_z3 = [&context, &var2expr](const auto& node, auto&& p) -> std::tuple<bool, z3::expr> {
1454  if (node.get_arity() != p.size())
1455  {
1456  return {false, z3::expr(context)};
1457  }
1458 
1459  switch (node.type)
1460  {
1462  return {true, context.bv_val(node.index, node.size)};
1464  // since our constants are defined as arbitrary bit-vectors,
1465  // we have to concat each bit just to be on the safe side
1466  auto constant = context.bv_val(node.constant.front(), 1);
1467  for (u32 i = 1; i < node.constant.size(); i++)
1468  {
1469  const auto bit = node.constant.at(i);
1470  constant = z3::concat(context.bv_val(bit, 1), constant);
1471  }
1472  return {true, constant};
1473  }
1475  if (auto it = var2expr.find(node.variable); it != var2expr.end())
1476  {
1477  return {true, it->second};
1478  }
1479  return {true, context.bv_const(node.variable.c_str(), node.size)};
1480  }
1481 
1483  return {true, p[0] & p[1]};
1485  return {true, p[0] | p[1]};
1487  return {true, ~p[0]};
1489  return {true, p[0] ^ p[1]};
1491  return {true, p[0].extract(p[2].get_numeral_uint(), p[1].get_numeral_uint())};
1493  return {true, z3::concat(p[0], p[1])};
1495  return {true, z3::sext(p[0], p[1].get_numeral_uint())};
1496 
1497  default:
1498  log_error("netlist", "Not implemented reached for nodetype {} in z3 conversion", node.type);
1499  return {false, z3::expr(context)};
1500  }
1501  };
1502 
1503  std::vector<z3::expr> stack;
1504  for (const auto& node : this->m_nodes)
1505  {
1506  std::vector<z3::expr> operands;
1507  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1508  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1509 
1510  if (auto [ok, reduction] = reduce_to_z3(node, std::move(operands)); ok)
1511  {
1512  stack.emplace_back(reduction);
1513  }
1514  else
1515  {
1516  return z3::expr(context);
1517  }
1518  }
1519 
1520  switch (stack.size())
1521  {
1522  case 1:
1523  return stack.back();
1524  default:
1525  return z3::expr(context);
1526  }
1527  }
1528 
1529  BooleanFunction::BooleanFunction(std::vector<BooleanFunction::Node>&& nodes) : m_nodes(nodes)
1530  {
1531  }
1532 
1533  BooleanFunction::BooleanFunction(BooleanFunction::Node&& node, std::vector<BooleanFunction>&& p) : BooleanFunction()
1534  {
1535  auto size = 1;
1536  for (const auto& parameter : p)
1537  {
1538  size += parameter.size();
1539  }
1540  this->m_nodes.reserve(size);
1541 
1542  for (auto&& parameter : p)
1543  {
1544  this->m_nodes.insert(this->m_nodes.end(), parameter.m_nodes.begin(), parameter.m_nodes.end());
1545  }
1546  this->m_nodes.emplace_back(node);
1547  }
1548 
1549  std::string BooleanFunction::to_string_in_reverse_polish_notation() const
1550  {
1551  std::string s;
1552  for (const auto& node : this->m_nodes)
1553  {
1554  s += node.to_string() + " ";
1555  }
1556  return s;
1557  }
1558 
1559  Result<BooleanFunction> BooleanFunction::validate(BooleanFunction&& function)
1560  {
1565  if (auto coverage = function.compute_node_coverage(); coverage.back() != function.length())
1566  {
1567  auto str = function.to_string_in_reverse_polish_notation();
1568  return ERR("could not validate '" + str + "': imbalanced function with coverage '" + std::to_string(coverage.back()) + " != " + std::to_string(function.length()));
1569  }
1570 
1571  return OK(std::move(function));
1572  }
1573 
1574  std::vector<u32> BooleanFunction::compute_node_coverage() const
1575  {
1576  auto coverage = std::vector<u32>(this->m_nodes.size(), (u32)-1);
1577 
1583  auto get = [](const auto& cov, size_t index) -> u32 { return (index < cov.size()) ? cov[index] : -1; };
1584 
1592  auto set = [](auto& cov, size_t index, u32 x = 0, u32 y = 0, u32 z = 0) { cov[index] = ((x != (u32)-1) && (y != (u32)-1) && (z != (u32)-1)) ? (x + y + z + 1) : (u32)-1; };
1593 
1594  for (auto i = 0ul; i < this->m_nodes.size(); i++)
1595  {
1596  auto arity = this->m_nodes[i].get_arity();
1597 
1598  switch (arity)
1599  {
1600  case 0: {
1601  set(coverage, i);
1602  break;
1603  }
1604  case 1: {
1605  auto x = get(coverage, i - 1);
1606  set(coverage, i, x);
1607  break;
1608  }
1609  case 2: {
1610  auto x = get(coverage, i - 1);
1611  auto y = get(coverage, i - 1 - x);
1612  set(coverage, i, x, y);
1613  break;
1614  }
1615  case 3: {
1616  auto x = get(coverage, i - 1);
1617  auto y = get(coverage, i - 1 - x);
1618  auto z = get(coverage, i - 1 - x - y);
1619  set(coverage, i, x, y, z);
1620  break;
1621  }
1622  }
1623  }
1624 
1625  return coverage;
1626  }
1627 
1629  {
1630  return Node(_type, _size, {}, {}, {});
1631  }
1632 
1633  BooleanFunction::Node BooleanFunction::Node::Constant(const std::vector<BooleanFunction::Value> _constant)
1634  {
1635  return Node(NodeType::Constant, _constant.size(), _constant, {}, {});
1636  }
1637 
1639  {
1640  return Node(NodeType::Index, _size, {}, _index, {});
1641  }
1642 
1643  BooleanFunction::Node BooleanFunction::Node::Variable(const std::string _variable, u16 _size)
1644  {
1645  return Node(NodeType::Variable, _size, {}, {}, _variable);
1646  }
1647 
1648  bool BooleanFunction::Node::operator==(const Node& other) const
1649  {
1650  return std::tie(this->type, this->size, this->constant, this->index, this->variable) == std::tie(other.type, other.size, other.constant, other.index, other.variable);
1651  }
1652 
1653  bool BooleanFunction::Node::operator!=(const Node& other) const
1654  {
1655  return !(*this == other);
1656  }
1657 
1658  bool BooleanFunction::Node::operator<(const Node& other) const
1659  {
1660  return std::tie(this->type, this->size, this->constant, this->index, this->variable) < std::tie(other.type, other.size, other.constant, other.index, other.variable);
1661  }
1662 
1664  {
1665  return Node(this->type, this->size, this->constant, this->index, this->variable);
1666  }
1667 
1669  {
1670  switch (this->type)
1671  {
1672  case NodeType::Constant: {
1673  std::string str;
1674  for (const auto& value : this->constant)
1675  {
1676  str = enum_to_string(value) + str;
1677  }
1678  return "0b" + str;
1679  }
1680 
1681  case NodeType::Index:
1682  return std::to_string(this->index);
1683  case NodeType::Variable:
1684  return this->variable;
1685 
1686  case NodeType::And:
1687  return "&";
1688  case NodeType::Or:
1689  return "|";
1690  case NodeType::Not:
1691  return "~";
1692  case NodeType::Xor:
1693  return "^";
1694 
1695  case NodeType::Add:
1696  return "+";
1697  case NodeType::Sub:
1698  return "-";
1699  case NodeType::Mul:
1700  return "*";
1701  case NodeType::Sdiv:
1702  return "/s";
1703  case NodeType::Udiv:
1704  return "/";
1705  case NodeType::Srem:
1706  return "\%s";
1707  case NodeType::Urem:
1708  return "\%";
1709 
1710  case NodeType::Concat:
1711  return "++";
1712  case NodeType::Slice:
1713  return "Slice";
1714  case NodeType::Zext:
1715  return "Zext";
1716  case NodeType::Sext:
1717  return "Sext";
1718 
1719  case NodeType::Shl:
1720  return "<<";
1721  case NodeType::Lshr:
1722  return ">>";
1723  case NodeType::Ashr:
1724  return ">>a";
1725  case NodeType::Rol:
1726  return "<<r";
1727  case NodeType::Ror:
1728  return ">>r";
1729 
1730  case NodeType::Eq:
1731  return "==";
1732  case NodeType::Sle:
1733  return "<=s";
1734  case NodeType::Slt:
1735  return "<s";
1736  case NodeType::Ule:
1737  return "<=";
1738  case NodeType::Ult:
1739  return "<";
1740  case NodeType::Ite:
1741  return "Ite";
1742 
1743  default:
1744  return "unsupported node type '" + std::to_string(this->type) + "'.";
1745  }
1746  }
1747 
1749  {
1751  }
1752 
1754  {
1755  static const std::map<u16, u16> type2arity = {
1764  };
1765 
1766  return type2arity.at(type);
1767  }
1768 
1769  bool BooleanFunction::Node::is(u16 _type) const
1770  {
1771  return this->type == _type;
1772  }
1773 
1775  {
1777  }
1778 
1779  bool BooleanFunction::Node::has_constant_value(const std::vector<Value>& value) const
1780  {
1781  return this->is_constant() && (this->constant == value);
1782  }
1783 
1785  {
1786  if (!this->is_constant())
1787  {
1788  return false;
1789  }
1790 
1791  auto bv_value = std::vector<BooleanFunction::Value>({});
1792  bv_value.reserve(this->size);
1793  for (auto i = 0u; i < this->constant.size(); i++)
1794  {
1795  bv_value.emplace_back((value & (1 << i)) ? BooleanFunction::Value::ONE : BooleanFunction::Value::ZERO);
1796  }
1797  return this->constant == bv_value;
1798  }
1799 
1801  {
1802  if (!this->is_constant())
1803  {
1804  return ERR("Node is not a constant");
1805  }
1806 
1807  return OK(this->constant);
1808  }
1809 
1811  {
1812  if (!this->is_constant())
1813  {
1814  return ERR("Node is not a constant");
1815  }
1816 
1817  if (this->size > 64)
1818  {
1819  return ERR("Node constant has size > 64");
1820  }
1821 
1822  if (std::any_of(this->constant.begin(), this->constant.end(), [](auto v) { return v != BooleanFunction::Value::ONE && v != BooleanFunction::Value::ZERO; }))
1823  {
1824  return ERR("Node constant is undefined or high-impedance");
1825  }
1826 
1827  u64 val = 0;
1828  for (auto it = this->constant.rbegin(); it != this->constant.rend(); it++)
1829  {
1830  val <<= 1;
1831  val |= *it;
1832  }
1833 
1834  return OK(val);
1835  }
1836 
1838  {
1839  return this->is(BooleanFunction::NodeType::Index);
1840  }
1841 
1843  {
1844  return this->is_index() && (this->index == value);
1845  }
1846 
1848  {
1849  if (!this->is_index())
1850  {
1851  return ERR("Node is not an index");
1852  }
1853 
1854  return OK(this->index);
1855  }
1856 
1858  {
1860  }
1861 
1862  bool BooleanFunction::Node::has_variable_name(const std::string& value) const
1863  {
1864  return this->is_variable() && (this->variable == value);
1865  }
1866 
1868  {
1869  if (!this->is_variable())
1870  {
1871  return ERR("Node is not a variable");
1872  }
1873 
1874  return OK(this->variable);
1875  }
1876 
1878  {
1879  return !this->is_operand();
1880  }
1881 
1883  {
1884  return this->is_constant() || this->is_variable() || this->is_index();
1885  }
1886 
1888  {
1889  return (this->type == NodeType::And) || (this->type == NodeType::Or) || (this->type == NodeType::Xor) || (this->type == NodeType::Add) || (this->type == NodeType::Mul)
1890  || (this->type == NodeType::Eq);
1891  }
1892 
1893  BooleanFunction::Node::Node(u16 _type, u16 _size, std::vector<BooleanFunction::Value> _constant, u16 _index, std::string _variable)
1894  : type(_type), size(_size), constant(_constant), index(_index), variable(_variable)
1895  {
1896  }
1897 
1898 } // namespace hal
u32 size
static Result< BooleanFunction > Slt(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool has_index_value(u16 index) const
BooleanFunction operator+(const BooleanFunction &other) const
static Result< BooleanFunction > Ite(BooleanFunction &&p0, BooleanFunction &&p1, BooleanFunction &&p2, u16 size)
BooleanFunction operator&(const BooleanFunction &other) const
static Result< BooleanFunction > Eq(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static BooleanFunction Var(const std::string &name, u16 size=1)
static Result< BooleanFunction > Xor(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool operator==(const BooleanFunction &other) const
Result< std::string > get_variable_name() const
BooleanFunction operator^(const BooleanFunction &other) const
const BooleanFunction::Node & get_top_level_node() const
static Result< BooleanFunction > Add(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
std::set< std::string > get_variable_names() const
static Result< BooleanFunction > Lshr(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool has_constant_value(const std::vector< Value > &value) const
static Result< BooleanFunction > Zext(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
Result< std::vector< Value > > get_constant_value() const
static Result< BooleanFunction > Mul(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool has_variable_name(const std::string &variable_name) const
static Result< BooleanFunction > Ule(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Sub(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Sext(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Udiv(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
BooleanFunction operator~() const
static Result< BooleanFunction > Concat(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Ult(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static BooleanFunction Index(u16 index, u16 size)
z3::expr to_z3(z3::context &context, const std::map< std::string, z3::expr > &var2expr={}) const
Result< std::vector< std::vector< Value > > > compute_truth_table(const std::vector< std::string > &ordered_variables={}, bool remove_unknown_variables=false) const
BooleanFunction & operator-=(const BooleanFunction &other)
Result< Value > evaluate(const std::unordered_map< std::string, Value > &inputs) const
static Result< BooleanFunction > Sle(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > build(std::vector< Node > &&nodes)
BooleanFunction operator|(const BooleanFunction &other) const
static Result< BooleanFunction > from_string(const std::string &expression)
static Result< BooleanFunction > Slice(BooleanFunction &&p0, BooleanFunction &&p1, BooleanFunction &&p2, u16 size)
const std::vector< BooleanFunction::Node > & get_nodes() const
BooleanFunction & operator*=(const BooleanFunction &other)
bool is(u16 type) const
BooleanFunction simplify() const
BooleanFunction operator-(const BooleanFunction &other) const
Result< std::string > get_truth_table_as_string(const std::vector< std::string > &ordered_variables={}, std::string function_name="", bool remove_unknown_variables=false) const
bool operator<(const BooleanFunction &other) const
BooleanFunction & operator&=(const BooleanFunction &other)
BooleanFunction clone() const
Value
represents the type of the node
static Result< BooleanFunction > Or(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
bool operator!=(const BooleanFunction &other) const
static Result< BooleanFunction > Urem(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
BooleanFunction & operator^=(const BooleanFunction &other)
static Result< BooleanFunction > Ror(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
BooleanFunction & operator+=(const BooleanFunction &other)
static Result< BooleanFunction > Sdiv(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
BooleanFunction & operator|=(const BooleanFunction &other)
static std::string to_string(Value value)
BooleanFunction simplify_local() const
static BooleanFunction Const(const BooleanFunction::Value &value)
BooleanFunction operator*(const BooleanFunction &other) const
static Result< BooleanFunction > Ashr(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< u64 > to_u64(const std::vector< BooleanFunction::Value > &value)
static Result< BooleanFunction > Not(BooleanFunction &&p0, u16 size)
static Result< BooleanFunction > And(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
std::vector< BooleanFunction > get_parameters() const
Result< u16 > get_index_value() const
BooleanFunction substitute(const std::string &old_variable_name, const std::string &new_variable_name) const
static Result< BooleanFunction > Shl(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
Result< u64 > get_constant_value_u64() const
static Result< BooleanFunction > Rol(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
static Result< BooleanFunction > Srem(BooleanFunction &&p0, BooleanFunction &&p1, u16 size)
uint64_t u64
Definition: defines.h:42
uint16_t u16
Definition: defines.h:40
uint32_t u32
Definition: defines.h:41
int64_t i64
Definition: defines.h:37
uint8_t u8
Definition: defines.h:39
#define log_error(channel,...)
Definition: log.h:78
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
#define ERR_APPEND(prev_error, message)
Definition: result.h:64
Result< std::vector< Token > > parse_with_standard_grammar(const std::string &expression)
Result< std::vector< Token > > parse_with_liberty_grammar(const std::string &expression)
ParserType
ParserType refers to the parser identifier.
Definition: parser.h:39
Result< BooleanFunction > translate(std::vector< Token > &&tokens, const std::string &expression)
Result< std::vector< Token > > reverse_polish_notation(std::vector< Token > &&tokens, const std::string &expression, const ParserType &parser)
Result< BooleanFunction > local_simplification(const BooleanFunction &function)
void remove(std::filesystem::path file_path)
Definition: defines.h:45
std::ostream & operator<<(std::ostream &os, T e)
Definition: enums.h:136
std::string enum_to_string(T e)
Definition: enums.h:53
PinType type
std::string name
std::vector< BooleanFunction::Value > constant
The (optional) constant value of the node.
u16 type
The type of the node.
u16 size
The bit-size of the node.
static u16 get_arity_of_type(u16 type)
static Node Constant(const std::vector< BooleanFunction::Value > value)
bool has_index_value(u16 value) const
std::string variable
The (optional) variable name of the node.
bool has_variable_name(const std::string &variable_name) const
static Node Operation(u16 type, u16 size)
bool has_constant_value(const std::vector< Value > &value) const
Result< std::vector< Value > > get_constant_value() const
static Node Index(u16 index, u16 size)
Result< u16 > get_index_value() const
bool operator!=(const Node &other) const
bool operator==(const Node &other) const
bool operator<(const Node &other) const
u16 index
The (optional) index value of the node.
static Node Variable(const std::string variable, u16 size)
Result< std::string > get_variable_name() const
Result< u64 > get_constant_value_u64() const
Token refers to a token identifier and accompanied data.
Definition: parser.h:67