HAL
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 
251  BooleanFunction BooleanFunction::Var(const std::string& name, u16 size)
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  {
280  return BooleanFunction(Node::Index(index, size));
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  return this->to_string_in_reverse_polish_notation() < other.to_string_in_reverse_polish_notation();
725  }
726 
728  {
729  return this->m_nodes.empty();
730  }
731 
733  {
734  auto function = BooleanFunction();
735  function.m_nodes.reserve(this->m_nodes.size());
736 
737  for (const auto& node : this->m_nodes)
738  {
739  function.m_nodes.emplace_back(node);
740  }
741 
742  return function;
743  }
744 
746  {
747  return this->get_top_level_node().size;
748  }
749 
751  {
752  return (this->is_empty()) ? false : this->get_top_level_node().is(type);
753  }
754 
756  {
757  return (this->is_empty()) ? false : this->get_top_level_node().is_variable();
758  }
759 
760  bool BooleanFunction::has_variable_name(const std::string& variable_name) const
761  {
762  return (this->is_empty()) ? false : this->get_top_level_node().has_variable_name(variable_name);
763  }
764 
766  {
767  if (this->is_empty())
768  {
769  return ERR("Boolean function is empty");
770  }
771 
772  return this->get_top_level_node().get_variable_name();
773  }
774 
776  {
777  return (this->is_empty()) ? false : this->get_top_level_node().is_constant();
778  }
779 
780  bool BooleanFunction::has_constant_value(const std::vector<Value>& value) const
781  {
782  return (this->is_empty()) ? false : this->get_top_level_node().has_constant_value(value);
783  }
784 
786  {
787  return (this->is_empty()) ? false : this->get_top_level_node().has_constant_value(value);
788  }
789 
791  {
792  if (this->is_empty())
793  {
794  return ERR("Boolean function is empty");
795  }
796 
797  return this->get_top_level_node().get_constant_value();
798  }
799 
801  {
802  if (this->is_empty())
803  {
804  return ERR("Boolean function is empty");
805  }
806 
808  }
809 
811  {
812  return (this->is_empty()) ? false : this->get_top_level_node().is_index();
813  }
814 
816  {
817  return (this->is_empty()) ? false : this->get_top_level_node().has_index_value(value);
818  }
819 
821  {
822  if (this->is_empty())
823  {
824  return ERR("Boolean function is empty");
825  }
826 
827  return this->get_top_level_node().get_index_value();
828  }
829 
831  {
832  return this->m_nodes.back();
833  }
834 
836  {
837  return this->m_nodes.size();
838  }
839 
840  const std::vector<BooleanFunction::Node>& BooleanFunction::get_nodes() const
841  {
842  return this->m_nodes;
843  }
844 
845  std::vector<BooleanFunction> BooleanFunction::get_parameters() const
846  {
853 
854  auto coverage = this->compute_node_coverage();
855  switch (this->get_top_level_node().get_arity())
856  {
857  case 0: {
858  return {};
859  }
860  case 1: {
861  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.end() - 1}))};
862  }
863  case 2: {
864  auto index = this->length() - coverage[this->length() - 2] - 1;
865 
866  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.begin() + index})),
867  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index, this->m_nodes.end() - 1}))};
868  }
869  case 3: {
870  auto index0 = this->length() - coverage[this->length() - 3] - coverage[this->length() - 2] - 1;
871  auto index1 = this->length() - coverage[this->length() - 2] - 1;
872 
873  return {BooleanFunction(std::vector<Node>({this->m_nodes.begin(), this->m_nodes.begin() + index0})),
874  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index0, this->m_nodes.begin() + index1})),
875  BooleanFunction(std::vector<Node>({this->m_nodes.begin() + index1, this->m_nodes.end() - 1}))};
876  }
877 
878  default:
879  assert(false && "not implemented reached.");
880  }
881 
882  return {};
883  }
884 
885  std::set<std::string> BooleanFunction::get_variable_names() const
886  {
887  auto variable_names = std::set<std::string>();
888  for (const auto& node : this->m_nodes)
889  {
890  if (node.is_variable())
891  {
892  variable_names.insert(node.variable);
893  }
894  }
895  return variable_names;
896  }
897 
898  Result<std::string> BooleanFunction::default_printer(const BooleanFunction::Node& node, std::vector<std::string>&& operands)
899  {
900  if (node.get_arity() != operands.size())
901  {
902  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()));
903  }
904 
905  switch (node.type)
906  {
910  return OK(node.to_string());
911 
913  return OK("(" + operands[0] + " & " + operands[1] + ")");
915  return OK("(! " + operands[0] + ")");
917  return OK("(" + operands[0] + " | " + operands[1] + ")");
919  return OK("(" + operands[0] + " ^ " + operands[1] + ")");
920 
922  return OK("(" + operands[0] + " + " + operands[1] + ")");
924  return OK("(" + operands[0] + " - " + operands[1] + ")");
926  return OK("(" + operands[0] + " * " + operands[1] + ")");
928  return OK("(" + operands[0] + " /s " + operands[1] + ")");
930  return OK("(" + operands[0] + " / " + operands[1] + ")");
932  return OK("(" + operands[0] + " \%s " + operands[1] + ")");
934  return OK("(" + operands[0] + " \% " + operands[1] + ")");
935 
937  return OK("(" + operands[0] + " ++ " + operands[1] + ")");
939  return OK("Slice(" + operands[0] + ", " + operands[1] + ", " + operands[2] + ")");
941  return OK("Zext(" + operands[0] + ", " + operands[1] + ")");
943  return OK("Sext(" + operands[0] + ", " + operands[1] + ")");
944 
946  return OK("(" + operands[0] + " << " + operands[1] + ")");
948  return OK("(" + operands[0] + " >> " + operands[1] + ")");
950  return OK("(" + operands[0] + " >>a " + operands[1] + ")");
952  return OK("(" + operands[0] + " <<r " + operands[1] + ")");
954  return OK("(" + operands[0] + " >>r " + operands[1] + ")");
955 
957  return OK("(" + operands[0] + " == " + operands[1] + ")");
959  return OK("(" + operands[0] + " <s " + operands[1] + ")");
961  return OK("(" + operands[0] + " <=s " + operands[1] + ")");
963  return OK("(" + operands[0] + " < " + operands[1] + ")");
965  return OK("(" + operands[0] + " <= " + operands[1] + ")");
967  return OK("Ite(" + operands[0] + ", " + operands[1] + ", " + operands[2] + ")");
968 
969  default:
970  return ERR("could not print Boolean function: unsupported node type '" + std::to_string(node.type) + "'");
971  }
972  }
973 
974  Result<std::string> BooleanFunction::algebraic_printer(const BooleanFunction::Node& node, std::vector<std::string>&& operands)
975  {
976  if (node.get_arity() != operands.size())
977  {
978  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()));
979  }
980 
981  switch (node.type)
982  {
985  return OK(node.to_string());
986 
988  return OK("CONST" + std::string(node.has_constant_value(0) ? "0" : "1"));
989 
991  return OK("(" + operands[0] + "*" + operands[1] + ")");
993  return OK("(! " + operands[0] + ")");
995  return OK("(" + operands[0] + "+" + operands[1] + ")");
996 
997  default:
998  return ERR("could not print Boolean function: unsupported node type '" + std::to_string(node.type) + "'");
999  }
1000  }
1001 
1002  std::string BooleanFunction::to_string(std::function<Result<std::string>(const BooleanFunction::Node& node, std::vector<std::string>&& operands)>&& printer) const
1003  {
1004  // (1) early termination in case the Boolean function is empty
1005  if (this->m_nodes.empty())
1006  {
1007  return "<empty>";
1008  }
1009 
1010  // (2) iterate the list of nodes and setup string from leafs to root
1011  std::vector<std::string> stack;
1012  for (const auto& node : this->m_nodes)
1013  {
1014  std::vector<std::string> operands;
1015 
1016  if (stack.size() < node.get_arity())
1017  {
1018  // 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());
1019  return "";
1020  }
1021 
1022  std::move(stack.end() - static_cast<u64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1023  stack.erase(stack.end() - static_cast<u64>(node.get_arity()), stack.end());
1024 
1025  if (auto res = printer(node, std::move(operands)); res.is_ok())
1026  {
1027  stack.emplace_back(res.get());
1028  }
1029  else
1030  {
1031  log_error("netlist", "Cannot translate BooleanFunction::Node '{}' to a string: {}.", node.to_string(), res.get_error().get());
1032  return "";
1033  }
1034  }
1035 
1036  switch (stack.size())
1037  {
1038  case 1:
1039  return stack.back();
1040  default: {
1041  // log_error("netlist", "Cannot translate BooleanFunction (= imbalanced stack with {} remaining parts).", stack.size());
1042  return "";
1043  }
1044  }
1045  }
1046 
1048  {
1051 
1052  static const std::vector<std::tuple<ParserType, std::function<Result<std::vector<Token>>(const std::string&)>>> parsers = {
1055  {ParserType::LibertyNoSpace, BooleanFunctionParser::parse_with_liberty_grammar}
1056  };
1057 
1058  for (const auto& [parser_type, parser] : parsers)
1059  {
1060  std::string sanitized_expression = expression;
1061  ParserType used_parser_type = parser_type;
1062 
1063  if (parser_type == ParserType::LibertyNoSpace)
1064  {
1065  sanitized_expression.erase(
1066  std::remove(sanitized_expression.begin(), sanitized_expression.end(), ' '),
1067  sanitized_expression.end()
1068  );
1069  used_parser_type = ParserType::Liberty;
1070  }
1071 
1072  auto tokens = parser(sanitized_expression);
1073  // (1) skip if parser cannot translate to tokens
1074  if (tokens.is_error())
1075  {
1076  continue;
1077  }
1078 
1079  // (2) skip if cannot translate to valid reverse-polish notation
1080  tokens = BooleanFunctionParser::reverse_polish_notation(tokens.get(), sanitized_expression, used_parser_type);
1081  if (tokens.is_error())
1082  {
1083  continue;
1084  }
1085  // (3) skip if reverse-polish notation tokens are no valid Boolean function
1086  auto function = BooleanFunctionParser::translate(tokens.get(), sanitized_expression);
1087  if (function.is_error())
1088  {
1089  continue;
1090  }
1091  return function;
1092  }
1093  return ERR("could not parse Boolean function from string: no parser available for '" + expression + "'");
1094  }
1095 
1097  {
1098  auto simplified = Simplification::local_simplification(*this).map<BooleanFunction>([](const auto& s) { return Simplification::abc_simplification(s); }).map<BooleanFunction>([](const auto& s) {
1100  });
1101 
1102  return (simplified.is_ok()) ? simplified.get() : this->clone();
1103  }
1104 
1106  {
1107  auto simplified = Simplification::local_simplification(*this);
1108 
1109  return (simplified.is_ok()) ? simplified.get() : this->clone();
1110  }
1111 
1112  BooleanFunction BooleanFunction::substitute(const std::string& old_variable_name, const std::string& new_variable_name) const
1113  {
1114  auto function = this->clone();
1115  for (auto i = 0u; i < this->m_nodes.size(); i++)
1116  {
1117  if (this->m_nodes[i].has_variable_name(old_variable_name))
1118  {
1119  function.m_nodes[i] = Node::Variable(new_variable_name, this->m_nodes[i].size);
1120  }
1121  }
1122 
1123  return function;
1124  }
1125 
1126  Result<BooleanFunction> BooleanFunction::substitute(const std::string& name, const BooleanFunction& replacement) const
1127  {
1128  // Helper function to substitute a variable with a Boolean function.
1129  auto substitute_variable = [](const auto& node, auto&& operands, auto var_name, auto repl) -> BooleanFunction {
1130  if (node.has_variable_name(var_name))
1131  {
1132  return repl.clone();
1133  }
1134  return BooleanFunction(node.clone(), std::move(operands));
1135  };
1136 
1137  std::vector<BooleanFunction> stack;
1138  for (const auto& node : this->m_nodes)
1139  {
1140  std::vector<BooleanFunction> operands;
1141  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1142  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1143 
1144  stack.emplace_back(substitute_variable(node, std::move(operands), name, replacement));
1145  }
1146 
1147  switch (stack.size())
1148  {
1149  case 1:
1150  return OK(stack.back());
1151  default:
1152  return ERR("could not replace variable '" + name + "' with Boolean function '" + replacement.to_string() + "': validation failed, the operations may be imbalanced");
1153  }
1154  }
1155 
1156  BooleanFunction BooleanFunction::substitute(const std::map<std::string, std::string>& substitutions) const
1157  {
1158  auto function = this->clone();
1159  for (auto i = 0u; i < this->m_nodes.size(); i++)
1160  {
1161  if (const auto var_name_res = this->m_nodes[i].get_variable_name(); var_name_res.is_ok())
1162  {
1163  if (const auto it = substitutions.find(var_name_res.get()); it != substitutions.end())
1164  {
1165  function.m_nodes[i] = Node::Variable(it->second, this->m_nodes[i].size);
1166  }
1167  }
1168  }
1169 
1170  return function;
1171  }
1172 
1173  Result<BooleanFunction> BooleanFunction::substitute(const std::map<std::string, BooleanFunction>& substitutions) const
1174  {
1180  auto substitute_variable = [substitutions](const auto& node, auto&& operands) -> BooleanFunction {
1181  if (node.is_variable())
1182  {
1183  if (auto repl_it = substitutions.find(node.variable); repl_it != substitutions.end())
1184  {
1185  return repl_it->second.clone();
1186  }
1187  }
1188  return BooleanFunction(node.clone(), std::move(operands));
1189  };
1190 
1191  std::vector<BooleanFunction> stack;
1192  for (const auto& node : this->m_nodes)
1193  {
1194  std::vector<BooleanFunction> operands;
1195  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1196  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1197 
1198  stack.emplace_back(substitute_variable(node, std::move(operands)));
1199  }
1200 
1201  switch (stack.size())
1202  {
1203  case 1:
1204  return OK(stack.back());
1205  default:
1206  return ERR("could not carry out multiple substitutions: validation failed, the operations may be imbalanced");
1207  }
1208  }
1209 
1210  Result<BooleanFunction::Value> BooleanFunction::evaluate(const std::unordered_map<std::string, Value>& inputs) const
1211  {
1212  // (0) workaround to preserve the API functionality
1213  if (this->m_nodes.empty())
1214  {
1215  return OK(BooleanFunction::Value::X);
1216  }
1217 
1218  // (1) validate whether the input sizes match the boolean function
1219  if (this->size() != 1)
1220  {
1221  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");
1222  }
1223 
1224  // (2) translate the input to n-bit to use the generic function
1225  auto generic_inputs = std::unordered_map<std::string, std::vector<Value>>();
1226  for (const auto& [name, value] : inputs)
1227  {
1228  generic_inputs.emplace(name, std::vector<Value>({value}));
1229  }
1230 
1231  auto value = this->evaluate(generic_inputs);
1232  if (value.is_ok())
1233  {
1234  // 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
1235  return OK(value.get()[0]);
1236  }
1237 
1238  return ERR(value.get_error());
1239  }
1240 
1241  Result<std::vector<BooleanFunction::Value>> BooleanFunction::evaluate(const std::unordered_map<std::string, std::vector<Value>>& inputs) const
1242  {
1243  // (0) workaround to preserve the API functionality
1244  if (this->m_nodes.empty())
1245  {
1246  return OK(std::vector<BooleanFunction::Value>({BooleanFunction::Value::X}));
1247  }
1248 
1249  // (1) validate whether the input sizes match the boolean function
1250  for (const auto& [name, value] : inputs)
1251  {
1252  for (const auto& node : this->m_nodes)
1253  {
1254  if (node.has_variable_name(name) && node.size != value.size())
1255  {
1256  return ERR("could not evaluate Boolean function '" + this->to_string() + "': as the size of vairbale " + name + " with size " + std::to_string(node.size)
1257  + " does not match the size of the provided input (" + std::to_string(value.size()) + ")");
1258  }
1259  }
1260  }
1261 
1262  // (2) initialize the symbolic state using the input variables
1263  auto symbolic_execution = SMT::SymbolicExecution();
1264  for (const auto& [name, value] : inputs)
1265  {
1266  symbolic_execution.state.set(BooleanFunction::Var(name, value.size()), BooleanFunction::Const(value));
1267  }
1268 
1269  // (3) analyze the evaluation result and check whether the result is a
1270  // constant boolean function
1271  auto result = symbolic_execution.evaluate(*this);
1272  if (result.is_ok())
1273  {
1274  if (auto value = result.get(); value.is_constant())
1275  {
1276  return OK(value.get_top_level_node().constant);
1277  }
1278  return OK(std::vector<BooleanFunction::Value>(this->size(), BooleanFunction::Value::X));
1279  }
1280  return ERR(result.get_error());
1281  }
1282 
1283  Result<std::vector<std::vector<BooleanFunction::Value>>> BooleanFunction::compute_truth_table(const std::vector<std::string>& ordered_variables, bool remove_unknown_variables) const
1284  {
1285  auto variable_names_in_function = this->get_variable_names();
1286 
1287  // (1) check that each variable is just a single bit, otherwise we do
1288  // not generate a truth-table
1289  for (const auto& node : this->m_nodes)
1290  {
1291  if (node.is_variable() && node.size != 1)
1292  {
1293  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");
1294  }
1295  }
1296 
1297  // (2) select either parameter or the Boolean function variables
1298  auto variables = ordered_variables;
1299  if (variables.empty())
1300  {
1301  variables = std::vector<std::string>(variable_names_in_function.begin(), variable_names_in_function.end());
1302  }
1303 
1304  // (3) remove any unknown variables from the truth table
1305  if (remove_unknown_variables)
1306  {
1307  variables.erase(
1308  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(); }),
1309  variables.end());
1310  }
1311 
1312  // (4.1) check that the function is not empty, otherwise we return a
1313  // Boolean function with a truth-table with 'X' values
1314  if (this->m_nodes.empty())
1315  {
1316  return OK(std::vector<std::vector<Value>>(1, std::vector<Value>(1 << variables.size(), Value::X)));
1317  }
1318 
1319  // (4.2) safety-check in case the number of variables is too large to process
1320  if (variables.size() > 10)
1321  {
1322  return ERR("could not compute truth table for Boolean function '" + this->to_string() + "': unable to generate truth-table with more than 10 variables");
1323  }
1324 
1325  std::vector<std::vector<Value>> truth_table(this->size(), std::vector<Value>(1 << variables.size(), Value::ZERO));
1326 
1327  // (5) iterate the truth-table rows and set each column accordingly
1328  for (auto value = 0u; value < ((u32)1 << variables.size()); value++)
1329  {
1330  std::unordered_map<std::string, std::vector<Value>> input;
1331  auto tmp = value;
1332  for (const auto& variable : variables)
1333  {
1334  input[variable] = ((tmp & 1) == 0) ? std::vector<Value>({Value::ZERO}) : std::vector<Value>({Value::ONE});
1335  tmp >>= 1;
1336  }
1337  auto result = this->evaluate(input);
1338  if (result.is_error())
1339  {
1340  return ERR(result.get_error());
1341  }
1342  auto output = result.get();
1343  for (auto index = 0u; index < truth_table.size(); index++)
1344  {
1345  truth_table[index][value] = output[index];
1346  }
1347  }
1348 
1349  return OK(truth_table);
1350  }
1351 
1352  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
1353  {
1354  std::vector<std::string> inputs;
1355  auto inputs_set = this->get_variable_names();
1356  if (ordered_inputs.empty())
1357  {
1358  inputs = std::vector<std::string>(inputs_set.begin(), inputs_set.end());
1359  }
1360  else
1361  {
1362  inputs = ordered_inputs;
1363  }
1364 
1365  if (remove_unknown_inputs)
1366  {
1367  inputs.erase(std::remove_if(inputs.begin(), inputs.end(), [&inputs_set](const auto& s) { return inputs_set.find(s) == inputs_set.end(); }), inputs.end());
1368  }
1369 
1370  const auto res = compute_truth_table(inputs, false);
1371  if (res.is_error())
1372  {
1373  return ERR_APPEND(res.get_error(), "could not print truth table for Boolean function '" + this->to_string() + "': unable to compute truth table");
1374  }
1375  const auto truth_table = res.get();
1376 
1377  std::stringstream str("");
1378 
1379  u32 num_inputs = inputs.size();
1380  u32 num_outputs = truth_table.size();
1381 
1382  // table headers
1383  std::vector<u32> in_widths;
1384  for (const auto& var : inputs)
1385  {
1386  in_widths.push_back(var.size());
1387  str << " " << var << " |";
1388  }
1389 
1390  std::vector<u32> out_widths;
1391  if (function_name.empty())
1392  {
1393  function_name = "O";
1394  }
1395  if (num_outputs == 1)
1396  {
1397  str << "| " << function_name << " ";
1398  out_widths.push_back(function_name.size());
1399  }
1400  else
1401  {
1402  for (u32 i = 0; i < num_outputs; i++)
1403  {
1404  std::string var = function_name + "(" + std::to_string(i) + ")";
1405  str << "| " << var << " ";
1406  out_widths.push_back(var.size());
1407  }
1408  }
1409  str << std::endl;
1410 
1411  // rule below headers
1412  for (u32 i = 0; i < num_inputs; i++)
1413  {
1414  str << std::setw(in_widths.at(i) + 3) << std::setfill('-') << "+";
1415  }
1416  for (u32 i = 0; i < num_outputs; i++)
1417  {
1418  str << "+" << std::setw(out_widths.at(i) + 2) << std::setfill('-') << "-";
1419  }
1420  str << std::endl;
1421 
1422  // table values
1423  for (u32 i = 0; i < (u32)(1 << num_inputs); i++)
1424  {
1425  for (u32 j = 0; j < num_inputs; j++)
1426  {
1427  str << " " << std::left << std::setw(in_widths.at(j)) << std::setfill(' ') << ((i >> j) & 1) << " |";
1428  }
1429 
1430  for (u32 k = 0; k < num_outputs; k++)
1431  {
1432  str << "| " << std::left << std::setw(out_widths.at(k)) << std::setfill(' ') << truth_table.at(k).at(i) << " ";
1433  }
1434  str << std::endl;
1435  }
1436  return OK(str.str());
1437  }
1438 
1439  z3::expr BooleanFunction::to_z3(z3::context& context, const std::map<std::string, z3::expr>& var2expr) const
1440  {
1447  auto reduce_to_z3 = [&context, &var2expr](const auto& node, auto&& p) -> std::tuple<bool, z3::expr> {
1448  if (node.get_arity() != p.size())
1449  {
1450  return {false, z3::expr(context)};
1451  }
1452 
1453  switch (node.type)
1454  {
1456  return {true, context.bv_val(node.index, node.size)};
1458  // since our constants are defined as arbitrary bit-vectors,
1459  // we have to concat each bit just to be on the safe side
1460  auto constant = context.bv_val(node.constant.front(), 1);
1461  for (u32 i = 1; i < node.constant.size(); i++)
1462  {
1463  const auto bit = node.constant.at(i);
1464  constant = z3::concat(context.bv_val(bit, 1), constant);
1465  }
1466  return {true, constant};
1467  }
1469  if (auto it = var2expr.find(node.variable); it != var2expr.end())
1470  {
1471  return {true, it->second};
1472  }
1473  return {true, context.bv_const(node.variable.c_str(), node.size)};
1474  }
1475 
1477  return {true, p[0] & p[1]};
1479  return {true, p[0] | p[1]};
1481  return {true, ~p[0]};
1483  return {true, p[0] ^ p[1]};
1485  return {true, p[0].extract(p[2].get_numeral_uint(), p[1].get_numeral_uint())};
1487  return {true, z3::concat(p[0], p[1])};
1489  return {true, z3::sext(p[0], p[1].get_numeral_uint())};
1490 
1491  default:
1492  log_error("netlist", "Not implemented reached for nodetype {} in z3 conversion", node.type);
1493  return {false, z3::expr(context)};
1494  }
1495  };
1496 
1497  std::vector<z3::expr> stack;
1498  for (const auto& node : this->m_nodes)
1499  {
1500  std::vector<z3::expr> operands;
1501  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1502  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1503 
1504  if (auto [ok, reduction] = reduce_to_z3(node, std::move(operands)); ok)
1505  {
1506  stack.emplace_back(reduction);
1507  }
1508  else
1509  {
1510  return z3::expr(context);
1511  }
1512  }
1513 
1514  switch (stack.size())
1515  {
1516  case 1:
1517  return stack.back();
1518  default:
1519  return z3::expr(context);
1520  }
1521  }
1522 
1523  BooleanFunction::BooleanFunction(std::vector<BooleanFunction::Node>&& nodes) : m_nodes(nodes)
1524  {
1525  }
1526 
1527  BooleanFunction::BooleanFunction(BooleanFunction::Node&& node, std::vector<BooleanFunction>&& p) : BooleanFunction()
1528  {
1529  auto size = 1;
1530  for (const auto& parameter : p)
1531  {
1532  size += parameter.size();
1533  }
1534  this->m_nodes.reserve(size);
1535 
1536  for (auto&& parameter : p)
1537  {
1538  this->m_nodes.insert(this->m_nodes.end(), parameter.m_nodes.begin(), parameter.m_nodes.end());
1539  }
1540  this->m_nodes.emplace_back(node);
1541  }
1542 
1543  std::string BooleanFunction::to_string_in_reverse_polish_notation() const
1544  {
1545  std::string s;
1546  for (const auto& node : this->m_nodes)
1547  {
1548  s += node.to_string() + " ";
1549  }
1550  return s;
1551  }
1552 
1553  Result<BooleanFunction> BooleanFunction::validate(BooleanFunction&& function)
1554  {
1559  if (auto coverage = function.compute_node_coverage(); coverage.back() != function.length())
1560  {
1561  auto str = function.to_string_in_reverse_polish_notation();
1562  return ERR("could not validate '" + str + "': imbalanced function with coverage '" + std::to_string(coverage.back()) + " != " + std::to_string(function.length()));
1563  }
1564 
1565  return OK(std::move(function));
1566  }
1567 
1568  std::vector<u32> BooleanFunction::compute_node_coverage() const
1569  {
1570  auto coverage = std::vector<u32>(this->m_nodes.size(), (u32)-1);
1571 
1577  auto get = [](const auto& cov, size_t index) -> u32 { return (index < cov.size()) ? cov[index] : -1; };
1578 
1586  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; };
1587 
1588  for (auto i = 0ul; i < this->m_nodes.size(); i++)
1589  {
1590  auto arity = this->m_nodes[i].get_arity();
1591 
1592  switch (arity)
1593  {
1594  case 0: {
1595  set(coverage, i);
1596  break;
1597  }
1598  case 1: {
1599  auto x = get(coverage, i - 1);
1600  set(coverage, i, x);
1601  break;
1602  }
1603  case 2: {
1604  auto x = get(coverage, i - 1);
1605  auto y = get(coverage, i - 1 - x);
1606  set(coverage, i, x, y);
1607  break;
1608  }
1609  case 3: {
1610  auto x = get(coverage, i - 1);
1611  auto y = get(coverage, i - 1 - x);
1612  auto z = get(coverage, i - 1 - x - y);
1613  set(coverage, i, x, y, z);
1614  break;
1615  }
1616  }
1617  }
1618 
1619  return coverage;
1620  }
1621 
1623  {
1624  return Node(_type, _size, {}, {}, {});
1625  }
1626 
1627  BooleanFunction::Node BooleanFunction::Node::Constant(const std::vector<BooleanFunction::Value> _constant)
1628  {
1629  return Node(NodeType::Constant, _constant.size(), _constant, {}, {});
1630  }
1631 
1633  {
1634  return Node(NodeType::Index, _size, {}, _index, {});
1635  }
1636 
1637  BooleanFunction::Node BooleanFunction::Node::Variable(const std::string _variable, u16 _size)
1638  {
1639  return Node(NodeType::Variable, _size, {}, {}, _variable);
1640  }
1641 
1642  bool BooleanFunction::Node::operator==(const Node& other) const
1643  {
1644  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);
1645  }
1646 
1647  bool BooleanFunction::Node::operator!=(const Node& other) const
1648  {
1649  return !(*this == other);
1650  }
1651 
1652  bool BooleanFunction::Node::operator<(const Node& other) const
1653  {
1654  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);
1655  }
1656 
1658  {
1659  return Node(this->type, this->size, this->constant, this->index, this->variable);
1660  }
1661 
1663  {
1664  switch (this->type)
1665  {
1666  case NodeType::Constant: {
1667  std::string str;
1668  for (const auto& value : this->constant)
1669  {
1670  str = enum_to_string(value) + str;
1671  }
1672  return "0b" + str;
1673  }
1674 
1675  case NodeType::Index:
1676  return std::to_string(this->index);
1677  case NodeType::Variable:
1678  return this->variable;
1679 
1680  case NodeType::And:
1681  return "&";
1682  case NodeType::Or:
1683  return "|";
1684  case NodeType::Not:
1685  return "~";
1686  case NodeType::Xor:
1687  return "^";
1688 
1689  case NodeType::Add:
1690  return "+";
1691  case NodeType::Sub:
1692  return "-";
1693  case NodeType::Mul:
1694  return "*";
1695  case NodeType::Sdiv:
1696  return "/s";
1697  case NodeType::Udiv:
1698  return "/";
1699  case NodeType::Srem:
1700  return "\%s";
1701  case NodeType::Urem:
1702  return "\%";
1703 
1704  case NodeType::Concat:
1705  return "++";
1706  case NodeType::Slice:
1707  return "Slice";
1708  case NodeType::Zext:
1709  return "Zext";
1710  case NodeType::Sext:
1711  return "Sext";
1712 
1713  case NodeType::Shl:
1714  return "<<";
1715  case NodeType::Lshr:
1716  return ">>";
1717  case NodeType::Ashr:
1718  return ">>a";
1719  case NodeType::Rol:
1720  return "<<r";
1721  case NodeType::Ror:
1722  return ">>r";
1723 
1724  case NodeType::Eq:
1725  return "==";
1726  case NodeType::Sle:
1727  return "<=s";
1728  case NodeType::Slt:
1729  return "<s";
1730  case NodeType::Ule:
1731  return "<=";
1732  case NodeType::Ult:
1733  return "<";
1734  case NodeType::Ite:
1735  return "Ite";
1736 
1737  default:
1738  return "unsupported node type '" + std::to_string(this->type) + "'.";
1739  }
1740  }
1741 
1743  {
1745  }
1746 
1748  {
1749  static const std::map<u16, u16> type2arity = {
1758  };
1759 
1760  return type2arity.at(type);
1761  }
1762 
1763  bool BooleanFunction::Node::is(u16 _type) const
1764  {
1765  return this->type == _type;
1766  }
1767 
1769  {
1771  }
1772 
1773  bool BooleanFunction::Node::has_constant_value(const std::vector<Value>& value) const
1774  {
1775  return this->is_constant() && (this->constant == value);
1776  }
1777 
1779  {
1780  if (!this->is_constant())
1781  {
1782  return false;
1783  }
1784 
1785  auto bv_value = std::vector<BooleanFunction::Value>({});
1786  bv_value.reserve(this->size);
1787  for (auto i = 0u; i < this->constant.size(); i++)
1788  {
1789  bv_value.emplace_back((value & (1 << i)) ? BooleanFunction::Value::ONE : BooleanFunction::Value::ZERO);
1790  }
1791  return this->constant == bv_value;
1792  }
1793 
1795  {
1796  if (!this->is_constant())
1797  {
1798  return ERR("Node is not a constant");
1799  }
1800 
1801  return OK(this->constant);
1802  }
1803 
1805  {
1806  if (!this->is_constant())
1807  {
1808  return ERR("Node is not a constant");
1809  }
1810 
1811  if (this->size > 64)
1812  {
1813  return ERR("Node constant has size > 64");
1814  }
1815 
1816  if (std::any_of(this->constant.begin(), this->constant.end(), [](auto v) { return v != BooleanFunction::Value::ONE && v != BooleanFunction::Value::ZERO; }))
1817  {
1818  return ERR("Node constant is undefined or high-impedance");
1819  }
1820 
1821  u64 val = 0;
1822  for (auto it = this->constant.rbegin(); it != this->constant.rend(); it++)
1823  {
1824  val <<= 1;
1825  val |= *it;
1826  }
1827 
1828  return OK(val);
1829  }
1830 
1832  {
1833  return this->is(BooleanFunction::NodeType::Index);
1834  }
1835 
1837  {
1838  return this->is_index() && (this->index == value);
1839  }
1840 
1842  {
1843  if (!this->is_index())
1844  {
1845  return ERR("Node is not an index");
1846  }
1847 
1848  return OK(this->index);
1849  }
1850 
1852  {
1854  }
1855 
1856  bool BooleanFunction::Node::has_variable_name(const std::string& value) const
1857  {
1858  return this->is_variable() && (this->variable == value);
1859  }
1860 
1862  {
1863  if (!this->is_variable())
1864  {
1865  return ERR("Node is not a variable");
1866  }
1867 
1868  return OK(this->variable);
1869  }
1870 
1872  {
1873  return !this->is_operand();
1874  }
1875 
1877  {
1878  return this->is_constant() || this->is_variable() || this->is_index();
1879  }
1880 
1882  {
1883  return (this->type == NodeType::And) || (this->type == NodeType::Or) || (this->type == NodeType::Xor) || (this->type == NodeType::Add) || (this->type == NodeType::Mul)
1884  || (this->type == NodeType::Eq);
1885  }
1886 
1887  BooleanFunction::Node::Node(u16 _type, u16 _size, std::vector<BooleanFunction::Value> _constant, u16 _index, std::string _variable)
1888  : type(_type), size(_size), constant(_constant), index(_index), variable(_variable)
1889  {
1890  }
1891 
1892 } // namespace hal
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
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:53
#define OK(...)
Definition: result.h:49
#define ERR_APPEND(prev_error, message)
Definition: result.h:57
parser
Definition: control.py:13
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:36
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)
std::ostream & operator<<(std::ostream &os, BooleanFunction::Value v)
std::string enum_to_string(T e)
Definition: enums.h:52
quint32 u32
PinType type
std::string name
include set(SRCROOT ${CMAKE_CURRENT_SOURCE_DIR}/src) set(UIROOT $
Definition: CMakeLists.txt:45
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:64