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  };
1056 
1057  for (const auto& [parser_type, parser] : parsers)
1058  {
1059  auto tokens = parser(expression);
1060  // (1) skip if parser cannot translate to tokens
1061  if (tokens.is_error())
1062  {
1063  continue;
1064  }
1065 
1066  // (2) skip if cannot translate to valid reverse-polish notation
1067  tokens = BooleanFunctionParser::reverse_polish_notation(tokens.get(), expression, parser_type);
1068  if (tokens.is_error())
1069  {
1070  continue;
1071  }
1072  // (3) skip if reverse-polish notation tokens are no valid Boolean function
1073  auto function = BooleanFunctionParser::translate(tokens.get(), expression);
1074  if (function.is_error())
1075  {
1076  continue;
1077  }
1078  return function;
1079  }
1080  return ERR("could not parse Boolean function from string: no parser available for '" + expression + "'");
1081  }
1082 
1084  {
1085  auto simplified = Simplification::local_simplification(*this).map<BooleanFunction>([](const auto& s) { return Simplification::abc_simplification(s); }).map<BooleanFunction>([](const auto& s) {
1087  });
1088 
1089  return (simplified.is_ok()) ? simplified.get() : this->clone();
1090  }
1091 
1093  {
1094  auto simplified = Simplification::local_simplification(*this);
1095 
1096  return (simplified.is_ok()) ? simplified.get() : this->clone();
1097  }
1098 
1099  BooleanFunction BooleanFunction::substitute(const std::string& old_variable_name, const std::string& new_variable_name) const
1100  {
1101  auto function = this->clone();
1102  for (auto i = 0u; i < this->m_nodes.size(); i++)
1103  {
1104  if (this->m_nodes[i].has_variable_name(old_variable_name))
1105  {
1106  function.m_nodes[i] = Node::Variable(new_variable_name, this->m_nodes[i].size);
1107  }
1108  }
1109 
1110  return function;
1111  }
1112 
1113  Result<BooleanFunction> BooleanFunction::substitute(const std::string& name, const BooleanFunction& replacement) const
1114  {
1115  // Helper function to substitute a variable with a Boolean function.
1116  auto substitute_variable = [](const auto& node, auto&& operands, auto var_name, auto repl) -> BooleanFunction {
1117  if (node.has_variable_name(var_name))
1118  {
1119  return repl.clone();
1120  }
1121  return BooleanFunction(node.clone(), std::move(operands));
1122  };
1123 
1124  std::vector<BooleanFunction> stack;
1125  for (const auto& node : this->m_nodes)
1126  {
1127  std::vector<BooleanFunction> operands;
1128  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1129  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1130 
1131  stack.emplace_back(substitute_variable(node, std::move(operands), name, replacement));
1132  }
1133 
1134  switch (stack.size())
1135  {
1136  case 1:
1137  return OK(stack.back());
1138  default:
1139  return ERR("could not replace variable '" + name + "' with Boolean function '" + replacement.to_string() + "': validation failed, the operations may be imbalanced");
1140  }
1141  }
1142 
1143  BooleanFunction BooleanFunction::substitute(const std::map<std::string, std::string>& substitutions) const
1144  {
1145  auto function = this->clone();
1146  for (auto i = 0u; i < this->m_nodes.size(); i++)
1147  {
1148  if (const auto var_name_res = this->m_nodes[i].get_variable_name(); var_name_res.is_ok())
1149  {
1150  if (const auto it = substitutions.find(var_name_res.get()); it != substitutions.end())
1151  {
1152  function.m_nodes[i] = Node::Variable(it->second, this->m_nodes[i].size);
1153  }
1154  }
1155  }
1156 
1157  return function;
1158  }
1159 
1160  Result<BooleanFunction> BooleanFunction::substitute(const std::map<std::string, BooleanFunction>& substitutions) const
1161  {
1167  auto substitute_variable = [substitutions](const auto& node, auto&& operands) -> BooleanFunction {
1168  if (node.is_variable())
1169  {
1170  if (auto repl_it = substitutions.find(node.variable); repl_it != substitutions.end())
1171  {
1172  return repl_it->second.clone();
1173  }
1174  }
1175  return BooleanFunction(node.clone(), std::move(operands));
1176  };
1177 
1178  std::vector<BooleanFunction> stack;
1179  for (const auto& node : this->m_nodes)
1180  {
1181  std::vector<BooleanFunction> operands;
1182  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1183  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1184 
1185  stack.emplace_back(substitute_variable(node, std::move(operands)));
1186  }
1187 
1188  switch (stack.size())
1189  {
1190  case 1:
1191  return OK(stack.back());
1192  default:
1193  return ERR("could not carry out multiple substitutions: validation failed, the operations may be imbalanced");
1194  }
1195  }
1196 
1197  Result<BooleanFunction::Value> BooleanFunction::evaluate(const std::unordered_map<std::string, Value>& inputs) const
1198  {
1199  // (0) workaround to preserve the API functionality
1200  if (this->m_nodes.empty())
1201  {
1202  return OK(BooleanFunction::Value::X);
1203  }
1204 
1205  // (1) validate whether the input sizes match the boolean function
1206  if (this->size() != 1)
1207  {
1208  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");
1209  }
1210 
1211  // (2) translate the input to n-bit to use the generic function
1212  auto generic_inputs = std::unordered_map<std::string, std::vector<Value>>();
1213  for (const auto& [name, value] : inputs)
1214  {
1215  generic_inputs.emplace(name, std::vector<Value>({value}));
1216  }
1217 
1218  auto value = this->evaluate(generic_inputs);
1219  if (value.is_ok())
1220  {
1221  // 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
1222  return OK(value.get()[0]);
1223  }
1224 
1225  return ERR(value.get_error());
1226  }
1227 
1228  Result<std::vector<BooleanFunction::Value>> BooleanFunction::evaluate(const std::unordered_map<std::string, std::vector<Value>>& inputs) const
1229  {
1230  // (0) workaround to preserve the API functionality
1231  if (this->m_nodes.empty())
1232  {
1233  return OK(std::vector<BooleanFunction::Value>({BooleanFunction::Value::X}));
1234  }
1235 
1236  // (1) validate whether the input sizes match the boolean function
1237  for (const auto& [name, value] : inputs)
1238  {
1239  for (const auto& node : this->m_nodes)
1240  {
1241  if (node.has_variable_name(name) && node.size != value.size())
1242  {
1243  return ERR("could not evaluate Boolean function '" + this->to_string() + "': as the size of vairbale " + name + " with size " + std::to_string(node.size)
1244  + " does not match the size of the provided input (" + std::to_string(value.size()) + ")");
1245  }
1246  }
1247  }
1248 
1249  // (2) initialize the symbolic state using the input variables
1250  auto symbolic_execution = SMT::SymbolicExecution();
1251  for (const auto& [name, value] : inputs)
1252  {
1253  symbolic_execution.state.set(BooleanFunction::Var(name, value.size()), BooleanFunction::Const(value));
1254  }
1255 
1256  // (3) analyze the evaluation result and check whether the result is a
1257  // constant boolean function
1258  auto result = symbolic_execution.evaluate(*this);
1259  if (result.is_ok())
1260  {
1261  if (auto value = result.get(); value.is_constant())
1262  {
1263  return OK(value.get_top_level_node().constant);
1264  }
1265  return OK(std::vector<BooleanFunction::Value>(this->size(), BooleanFunction::Value::X));
1266  }
1267  return ERR(result.get_error());
1268  }
1269 
1270  Result<std::vector<std::vector<BooleanFunction::Value>>> BooleanFunction::compute_truth_table(const std::vector<std::string>& ordered_variables, bool remove_unknown_variables) const
1271  {
1272  auto variable_names_in_function = this->get_variable_names();
1273 
1274  // (1) check that each variable is just a single bit, otherwise we do
1275  // not generate a truth-table
1276  for (const auto& node : this->m_nodes)
1277  {
1278  if (node.is_variable() && node.size != 1)
1279  {
1280  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");
1281  }
1282  }
1283 
1284  // (2) select either parameter or the Boolean function variables
1285  auto variables = ordered_variables;
1286  if (variables.empty())
1287  {
1288  variables = std::vector<std::string>(variable_names_in_function.begin(), variable_names_in_function.end());
1289  }
1290 
1291  // (3) remove any unknown variables from the truth table
1292  if (remove_unknown_variables)
1293  {
1294  variables.erase(
1295  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(); }),
1296  variables.end());
1297  }
1298 
1299  // (4.1) check that the function is not empty, otherwise we return a
1300  // Boolean function with a truth-table with 'X' values
1301  if (this->m_nodes.empty())
1302  {
1303  return OK(std::vector<std::vector<Value>>(1, std::vector<Value>(1 << variables.size(), Value::X)));
1304  }
1305 
1306  // (4.2) safety-check in case the number of variables is too large to process
1307  if (variables.size() > 10)
1308  {
1309  return ERR("could not compute truth table for Boolean function '" + this->to_string() + "': unable to generate truth-table with more than 10 variables");
1310  }
1311 
1312  std::vector<std::vector<Value>> truth_table(this->size(), std::vector<Value>(1 << variables.size(), Value::ZERO));
1313 
1314  // (5) iterate the truth-table rows and set each column accordingly
1315  for (auto value = 0u; value < ((u32)1 << variables.size()); value++)
1316  {
1317  std::unordered_map<std::string, std::vector<Value>> input;
1318  auto tmp = value;
1319  for (const auto& variable : variables)
1320  {
1321  input[variable] = ((tmp & 1) == 0) ? std::vector<Value>({Value::ZERO}) : std::vector<Value>({Value::ONE});
1322  tmp >>= 1;
1323  }
1324  auto result = this->evaluate(input);
1325  if (result.is_error())
1326  {
1327  return ERR(result.get_error());
1328  }
1329  auto output = result.get();
1330  for (auto index = 0u; index < truth_table.size(); index++)
1331  {
1332  truth_table[index][value] = output[index];
1333  }
1334  }
1335 
1336  return OK(truth_table);
1337  }
1338 
1339  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
1340  {
1341  std::vector<std::string> inputs;
1342  auto inputs_set = this->get_variable_names();
1343  if (ordered_inputs.empty())
1344  {
1345  inputs = std::vector<std::string>(inputs_set.begin(), inputs_set.end());
1346  }
1347  else
1348  {
1349  inputs = ordered_inputs;
1350  }
1351 
1352  if (remove_unknown_inputs)
1353  {
1354  inputs.erase(std::remove_if(inputs.begin(), inputs.end(), [&inputs_set](const auto& s) { return inputs_set.find(s) == inputs_set.end(); }), inputs.end());
1355  }
1356 
1357  const auto res = compute_truth_table(inputs, false);
1358  if (res.is_error())
1359  {
1360  return ERR_APPEND(res.get_error(), "could not print truth table for Boolean function '" + this->to_string() + "': unable to compute truth table");
1361  }
1362  const auto truth_table = res.get();
1363 
1364  std::stringstream str("");
1365 
1366  u32 num_inputs = inputs.size();
1367  u32 num_outputs = truth_table.size();
1368 
1369  // table headers
1370  std::vector<u32> in_widths;
1371  for (const auto& var : inputs)
1372  {
1373  in_widths.push_back(var.size());
1374  str << " " << var << " |";
1375  }
1376 
1377  std::vector<u32> out_widths;
1378  if (function_name.empty())
1379  {
1380  function_name = "O";
1381  }
1382  if (num_outputs == 1)
1383  {
1384  str << "| " << function_name << " ";
1385  out_widths.push_back(function_name.size());
1386  }
1387  else
1388  {
1389  for (u32 i = 0; i < num_outputs; i++)
1390  {
1391  std::string var = function_name + "(" + std::to_string(i) + ")";
1392  str << "| " << var << " ";
1393  out_widths.push_back(var.size());
1394  }
1395  }
1396  str << std::endl;
1397 
1398  // rule below headers
1399  for (u32 i = 0; i < num_inputs; i++)
1400  {
1401  str << std::setw(in_widths.at(i) + 3) << std::setfill('-') << "+";
1402  }
1403  for (u32 i = 0; i < num_outputs; i++)
1404  {
1405  str << "+" << std::setw(out_widths.at(i) + 2) << std::setfill('-') << "-";
1406  }
1407  str << std::endl;
1408 
1409  // table values
1410  for (u32 i = 0; i < (u32)(1 << num_inputs); i++)
1411  {
1412  for (u32 j = 0; j < num_inputs; j++)
1413  {
1414  str << " " << std::left << std::setw(in_widths.at(j)) << std::setfill(' ') << ((i >> j) & 1) << " |";
1415  }
1416 
1417  for (u32 k = 0; k < num_outputs; k++)
1418  {
1419  str << "| " << std::left << std::setw(out_widths.at(k)) << std::setfill(' ') << truth_table.at(k).at(i) << " ";
1420  }
1421  str << std::endl;
1422  }
1423  return OK(str.str());
1424  }
1425 
1426  z3::expr BooleanFunction::to_z3(z3::context& context, const std::map<std::string, z3::expr>& var2expr) const
1427  {
1434  auto reduce_to_z3 = [&context, &var2expr](const auto& node, auto&& p) -> std::tuple<bool, z3::expr> {
1435  if (node.get_arity() != p.size())
1436  {
1437  return {false, z3::expr(context)};
1438  }
1439 
1440  switch (node.type)
1441  {
1443  return {true, context.bv_val(node.index, node.size)};
1445  // since our constants are defined as arbitrary bit-vectors,
1446  // we have to concat each bit just to be on the safe side
1447  auto constant = context.bv_val(node.constant.front(), 1);
1448  for (u32 i = 1; i < node.constant.size(); i++)
1449  {
1450  const auto bit = node.constant.at(i);
1451  constant = z3::concat(context.bv_val(bit, 1), constant);
1452  }
1453  return {true, constant};
1454  }
1456  if (auto it = var2expr.find(node.variable); it != var2expr.end())
1457  {
1458  return {true, it->second};
1459  }
1460  return {true, context.bv_const(node.variable.c_str(), node.size)};
1461  }
1462 
1464  return {true, p[0] & p[1]};
1466  return {true, p[0] | p[1]};
1468  return {true, ~p[0]};
1470  return {true, p[0] ^ p[1]};
1472  return {true, p[0].extract(p[2].get_numeral_uint(), p[1].get_numeral_uint())};
1474  return {true, z3::concat(p[0], p[1])};
1476  return {true, z3::sext(p[0], p[1].get_numeral_uint())};
1477 
1478  default:
1479  log_error("netlist", "Not implemented reached for nodetype {} in z3 conversion", node.type);
1480  return {false, z3::expr(context)};
1481  }
1482  };
1483 
1484  std::vector<z3::expr> stack;
1485  for (const auto& node : this->m_nodes)
1486  {
1487  std::vector<z3::expr> operands;
1488  std::move(stack.end() - static_cast<i64>(node.get_arity()), stack.end(), std::back_inserter(operands));
1489  stack.erase(stack.end() - static_cast<i64>(node.get_arity()), stack.end());
1490 
1491  if (auto [ok, reduction] = reduce_to_z3(node, std::move(operands)); ok)
1492  {
1493  stack.emplace_back(reduction);
1494  }
1495  else
1496  {
1497  return z3::expr(context);
1498  }
1499  }
1500 
1501  switch (stack.size())
1502  {
1503  case 1:
1504  return stack.back();
1505  default:
1506  return z3::expr(context);
1507  }
1508  }
1509 
1510  BooleanFunction::BooleanFunction(std::vector<BooleanFunction::Node>&& nodes) : m_nodes(nodes)
1511  {
1512  }
1513 
1514  BooleanFunction::BooleanFunction(BooleanFunction::Node&& node, std::vector<BooleanFunction>&& p) : BooleanFunction()
1515  {
1516  auto size = 1;
1517  for (const auto& parameter : p)
1518  {
1519  size += parameter.size();
1520  }
1521  this->m_nodes.reserve(size);
1522 
1523  for (auto&& parameter : p)
1524  {
1525  this->m_nodes.insert(this->m_nodes.end(), parameter.m_nodes.begin(), parameter.m_nodes.end());
1526  }
1527  this->m_nodes.emplace_back(node);
1528  }
1529 
1530  std::string BooleanFunction::to_string_in_reverse_polish_notation() const
1531  {
1532  std::string s;
1533  for (const auto& node : this->m_nodes)
1534  {
1535  s += node.to_string() + " ";
1536  }
1537  return s;
1538  }
1539 
1540  Result<BooleanFunction> BooleanFunction::validate(BooleanFunction&& function)
1541  {
1546  if (auto coverage = function.compute_node_coverage(); coverage.back() != function.length())
1547  {
1548  auto str = function.to_string_in_reverse_polish_notation();
1549  return ERR("could not validate '" + str + "': imbalanced function with coverage '" + std::to_string(coverage.back()) + " != " + std::to_string(function.length()));
1550  }
1551 
1552  return OK(std::move(function));
1553  }
1554 
1555  std::vector<u32> BooleanFunction::compute_node_coverage() const
1556  {
1557  auto coverage = std::vector<u32>(this->m_nodes.size(), (u32)-1);
1558 
1564  auto get = [](const auto& cov, size_t index) -> u32 { return (index < cov.size()) ? cov[index] : -1; };
1565 
1573  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; };
1574 
1575  for (auto i = 0ul; i < this->m_nodes.size(); i++)
1576  {
1577  auto arity = this->m_nodes[i].get_arity();
1578 
1579  switch (arity)
1580  {
1581  case 0: {
1582  set(coverage, i);
1583  break;
1584  }
1585  case 1: {
1586  auto x = get(coverage, i - 1);
1587  set(coverage, i, x);
1588  break;
1589  }
1590  case 2: {
1591  auto x = get(coverage, i - 1);
1592  auto y = get(coverage, i - 1 - x);
1593  set(coverage, i, x, y);
1594  break;
1595  }
1596  case 3: {
1597  auto x = get(coverage, i - 1);
1598  auto y = get(coverage, i - 1 - x);
1599  auto z = get(coverage, i - 1 - x - y);
1600  set(coverage, i, x, y, z);
1601  break;
1602  }
1603  }
1604  }
1605 
1606  return coverage;
1607  }
1608 
1610  {
1611  return Node(_type, _size, {}, {}, {});
1612  }
1613 
1614  BooleanFunction::Node BooleanFunction::Node::Constant(const std::vector<BooleanFunction::Value> _constant)
1615  {
1616  return Node(NodeType::Constant, _constant.size(), _constant, {}, {});
1617  }
1618 
1620  {
1621  return Node(NodeType::Index, _size, {}, _index, {});
1622  }
1623 
1624  BooleanFunction::Node BooleanFunction::Node::Variable(const std::string _variable, u16 _size)
1625  {
1626  return Node(NodeType::Variable, _size, {}, {}, _variable);
1627  }
1628 
1629  bool BooleanFunction::Node::operator==(const Node& other) const
1630  {
1631  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);
1632  }
1633 
1634  bool BooleanFunction::Node::operator!=(const Node& other) const
1635  {
1636  return !(*this == other);
1637  }
1638 
1639  bool BooleanFunction::Node::operator<(const Node& other) const
1640  {
1641  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);
1642  }
1643 
1645  {
1646  return Node(this->type, this->size, this->constant, this->index, this->variable);
1647  }
1648 
1650  {
1651  switch (this->type)
1652  {
1653  case NodeType::Constant: {
1654  std::string str;
1655  for (const auto& value : this->constant)
1656  {
1657  str = enum_to_string(value) + str;
1658  }
1659  return "0b" + str;
1660  }
1661 
1662  case NodeType::Index:
1663  return std::to_string(this->index);
1664  case NodeType::Variable:
1665  return this->variable;
1666 
1667  case NodeType::And:
1668  return "&";
1669  case NodeType::Or:
1670  return "|";
1671  case NodeType::Not:
1672  return "~";
1673  case NodeType::Xor:
1674  return "^";
1675 
1676  case NodeType::Add:
1677  return "+";
1678  case NodeType::Sub:
1679  return "-";
1680  case NodeType::Mul:
1681  return "*";
1682  case NodeType::Sdiv:
1683  return "/s";
1684  case NodeType::Udiv:
1685  return "/";
1686  case NodeType::Srem:
1687  return "\%s";
1688  case NodeType::Urem:
1689  return "\%";
1690 
1691  case NodeType::Concat:
1692  return "++";
1693  case NodeType::Slice:
1694  return "Slice";
1695  case NodeType::Zext:
1696  return "Zext";
1697  case NodeType::Sext:
1698  return "Sext";
1699 
1700  case NodeType::Shl:
1701  return "<<";
1702  case NodeType::Lshr:
1703  return ">>";
1704  case NodeType::Ashr:
1705  return ">>a";
1706  case NodeType::Rol:
1707  return "<<r";
1708  case NodeType::Ror:
1709  return ">>r";
1710 
1711  case NodeType::Eq:
1712  return "==";
1713  case NodeType::Sle:
1714  return "<=s";
1715  case NodeType::Slt:
1716  return "<s";
1717  case NodeType::Ule:
1718  return "<=";
1719  case NodeType::Ult:
1720  return "<";
1721  case NodeType::Ite:
1722  return "Ite";
1723 
1724  default:
1725  return "unsupported node type '" + std::to_string(this->type) + "'.";
1726  }
1727  }
1728 
1730  {
1732  }
1733 
1735  {
1736  static const std::map<u16, u16> type2arity = {
1745  };
1746 
1747  return type2arity.at(type);
1748  }
1749 
1750  bool BooleanFunction::Node::is(u16 _type) const
1751  {
1752  return this->type == _type;
1753  }
1754 
1756  {
1758  }
1759 
1760  bool BooleanFunction::Node::has_constant_value(const std::vector<Value>& value) const
1761  {
1762  return this->is_constant() && (this->constant == value);
1763  }
1764 
1766  {
1767  if (!this->is_constant())
1768  {
1769  return false;
1770  }
1771 
1772  auto bv_value = std::vector<BooleanFunction::Value>({});
1773  bv_value.reserve(this->size);
1774  for (auto i = 0u; i < this->constant.size(); i++)
1775  {
1776  bv_value.emplace_back((value & (1 << i)) ? BooleanFunction::Value::ONE : BooleanFunction::Value::ZERO);
1777  }
1778  return this->constant == bv_value;
1779  }
1780 
1782  {
1783  if (!this->is_constant())
1784  {
1785  return ERR("Node is not a constant");
1786  }
1787 
1788  return OK(this->constant);
1789  }
1790 
1792  {
1793  if (!this->is_constant())
1794  {
1795  return ERR("Node is not a constant");
1796  }
1797 
1798  if (this->size > 64)
1799  {
1800  return ERR("Node constant has size > 64");
1801  }
1802 
1803  if (std::any_of(this->constant.begin(), this->constant.end(), [](auto v) { return v != BooleanFunction::Value::ONE && v != BooleanFunction::Value::ZERO; }))
1804  {
1805  return ERR("Node constant is undefined or high-impedance");
1806  }
1807 
1808  u64 val = 0;
1809  for (auto it = this->constant.rbegin(); it != this->constant.rend(); it++)
1810  {
1811  val <<= 1;
1812  val |= *it;
1813  }
1814 
1815  return OK(val);
1816  }
1817 
1819  {
1820  return this->is(BooleanFunction::NodeType::Index);
1821  }
1822 
1824  {
1825  return this->is_index() && (this->index == value);
1826  }
1827 
1829  {
1830  if (!this->is_index())
1831  {
1832  return ERR("Node is not an index");
1833  }
1834 
1835  return OK(this->index);
1836  }
1837 
1839  {
1841  }
1842 
1843  bool BooleanFunction::Node::has_variable_name(const std::string& value) const
1844  {
1845  return this->is_variable() && (this->variable == value);
1846  }
1847 
1849  {
1850  if (!this->is_variable())
1851  {
1852  return ERR("Node is not a variable");
1853  }
1854 
1855  return OK(this->variable);
1856  }
1857 
1859  {
1860  return !this->is_operand();
1861  }
1862 
1864  {
1865  return this->is_constant() || this->is_variable() || this->is_index();
1866  }
1867 
1869  {
1870  return (this->type == NodeType::And) || (this->type == NodeType::Or) || (this->type == NodeType::Xor) || (this->type == NodeType::Add) || (this->type == NodeType::Mul)
1871  || (this->type == NodeType::Eq);
1872  }
1873 
1874  BooleanFunction::Node::Node(u16 _type, u16 _size, std::vector<BooleanFunction::Value> _constant, u16 _index, std::string _variable)
1875  : type(_type), size(_size), constant(_constant), index(_index), variable(_variable)
1876  {
1877  }
1878 
1879 } // 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)
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:62