HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
create_functional_candidates.cpp
Go to the documentation of this file.
6 
7 namespace hal
8 {
9  namespace module_identification
10  {
11  //comparisons
12 
13  namespace
14  {
15  // creates a sorted list of Nets pairing 2 nets with close to same influence together for leq and eq comparisons
16  Result<std::vector<std::pair<Net*, Net*>>> create_net_pair_sorting_by_influence(CandidateContext& ctx, const Net* output_net, const std::map<Net*, BooleanFunction::Value>& initial_mapping)
17  {
18  const Netlist* nl = output_net->get_netlist();
19 
20  auto to_use_control_mapping = initial_mapping;
21 
22  std::vector<std::pair<Net*, Net*>> result;
23  while (true)
24  {
25  const auto res = ctx.get_boolean_influence(output_net, to_use_control_mapping);
26  if (res.is_error())
27  {
28  return ERR_APPEND(res.get_error(), "failed to create boolean influence");
29  }
30  const auto influence_mapping = res.get();
31 
32  // get 5 pairs with the highest influence as they are ordered enough
33  std::vector<std::pair<Net*, double>> influence_vector;
34  for (std::pair<std::string, double> cur_pair : influence_mapping)
35  {
36  Net* cur_net = BooleanFunctionNetDecorator::get_net_from(nl, cur_pair.first).get();
37  influence_vector.push_back(std::make_pair(cur_net, cur_pair.second));
38  }
39 
40  // if at any point the influence vector does not contain an even amount of nets we can stop
41  if ((influence_vector.size() % 2) != 0)
42  {
43  return OK({});
44  }
45 
46  // sort the values to take first 10
47  std::sort(influence_vector.begin(), influence_vector.end(), [](const std::pair<Net*, double>& a, const std::pair<Net*, double>& b) { return a.second > b.second; });
48 
49  const u32 upper_bound = std::min((u32)influence_vector.size(), (u32)10);
50  for (u32 i = 0; i < upper_bound; i += 2)
51  {
52  to_use_control_mapping[influence_vector[i].first] = BooleanFunction::Value::ZERO;
53  to_use_control_mapping[influence_vector[i + 1].first] = BooleanFunction::Value::ZERO;
54 
55  result.push_back(std::make_pair(influence_vector[i].first, influence_vector[i + 1].first));
56  }
57 
58  if (influence_vector.size() <= 10)
59  {
60  break;
61  }
62  }
63 
64  std::reverse(result.begin(), result.end());
65 
66  return OK(result);
67  }
68  } // namespace
69 
71  {
72  UNUSED(ctx);
73  std::vector<FunctionalCandidate> result;
74  if (candidate.m_output_nets.size() > 2 || candidate.m_gates.empty())
75  {
76  return OK(std::vector<FunctionalCandidate>());
77  }
78  for (Net* cur_out_net : candidate.m_output_nets)
79  {
80  auto new_candidate = FunctionalCandidate(candidate);
81  new_candidate.m_output_nets = {cur_out_net};
82 
83  result.push_back(new_candidate);
84  }
85  return OK(result);
86  }
87 
89  {
90  std::vector<FunctionalCandidate> candidates;
91  const auto var_names_res = ctx.get_variable_names(candidate.m_output_nets.front(), candidate.m_control_mapping);
92  if (var_names_res.is_error())
93  {
94  return ERR_APPEND(var_names_res.get_error(), "cannot check whether to discard equal candidate: failed to retrieve variable names from context");
95  }
96  const auto& var_names = var_names_res.get();
97 
98  if (var_names.size() % 2 == 0)
99  {
100  candidates.push_back(FunctionalCandidate(candidate));
101  }
102  return OK(candidates);
103  }
104 
106  {
107  std::vector<FunctionalCandidate> candidates = {FunctionalCandidate(candidate)};
108  const Netlist* nl = candidate.m_gates.front()->get_netlist();
109 
110  auto res = ctx.get_boolean_influence(candidate.m_output_nets[0], candidate.m_control_mapping);
111  if (res.is_error())
112  {
113  return ERR_APPEND(res.get_error(), "cannot find control signals via Boolean influence: failed Boolean influence calculation");
114  }
115  std::unordered_map<std::string, double> influence_mapping = res.get();
116 
117  std::vector<std::pair<Net*, double>> influence_vector;
118  for (std::pair<std::string, double> cur_pair : influence_mapping)
119  {
120  Net* cur_net = BooleanFunctionNetDecorator::get_net_from(nl, cur_pair.first).get();
121  influence_vector.push_back(std::make_pair(cur_net, cur_pair.second));
122  }
123 
124  // sort the values to categorize the signal of the highest influence as control signal
125  std::sort(influence_vector.begin(), influence_vector.end(), [](const std::pair<Net*, double>& a, const std::pair<Net*, double>& b) { return a.second > b.second; });
126  // const u32 max_control_signals = std::min(candidate.m_max_control_signals, u32(influence_vector.size() - 1));
127  const u32 max_control_signals = std::min(candidate.m_max_control_signals, u32(1));
128  for (u32 num_control_signals = 0; num_control_signals < max_control_signals; num_control_signals++)
129  {
130  FunctionalCandidate new_candidate = FunctionalCandidate(candidate);
131  std::vector<Net*> control_signals;
132  for (u32 net_idx = 0; net_idx < num_control_signals + 1; net_idx++)
133  {
134  control_signals.push_back(influence_vector[net_idx].first);
135  }
136 
137  new_candidate.m_control_signals = control_signals;
138  candidates.push_back(new_candidate);
139  }
140 
141  return OK(candidates);
142  }
143 
145  {
146  std::vector<FunctionalCandidate> new_candidates = {FunctionalCandidate(candidate)};
147 
148  const Netlist* nl = candidate.m_gates[0]->get_netlist();
149  auto res = ctx.get_boolean_influence(candidate.m_output_nets[0], candidate.m_control_mapping);
150  if (res.is_error())
151  {
152  return ERR_APPEND(res.get_error(), ("failed to get boolean influence for net " + candidate.m_output_nets[0]->get_name()));
153  }
154  auto influences = res.get();
155 
156  // convert influence to vector of nets
157  std::vector<std::pair<Net*, double>> influence_vector;
158  for (const auto& cur_pair : influences)
159  {
160  Net* cur_net = BooleanFunctionNetDecorator::get_net_from(nl, cur_pair.first).get();
161  influence_vector.push_back(std::make_pair(cur_net, cur_pair.second));
162  }
163 
164  if (influence_vector.size() < 3)
165  {
166  return OK(new_candidates);
167  }
168 
169  // sort the values to find the nets with the biggest influence
170  std::sort(influence_vector.begin(), influence_vector.end(), [](const std::pair<Net*, double>& a, const std::pair<Net*, double>& b) { return a.second > b.second; });
171 
172  // sign extended variables are expected to be about 2 times the influence of the next bit
173  if ((influence_vector[0].second * 0.75 < influence_vector[1].second))
174  {
175  return OK(new_candidates);
176  }
177 
178  auto new_candidate = FunctionalCandidate(candidate);
179  new_candidate.m_sign_nets.push_back(influence_vector[0].first);
180 
181  // all extension bits basicly have about the same influence after the first
182  for (u32 net_idx = 1; net_idx < (influence_vector.size() - 1); net_idx++)
183  {
184  new_candidate.m_sign_nets.push_back(influence_vector[net_idx].first);
185 
186  if ((influence_vector[net_idx].second * 0.75) > influence_vector[net_idx + 1].second)
187  {
188  break;
189  }
190  }
191 
192  // we dont allow more than 6 sign extension bits for complexity
193  if (new_candidate.m_sign_nets.size() > 6)
194  {
195  return OK(new_candidates);
196  }
197 
198  // sign extend in a way that the remaining operands are the same size
199  if (((influence_vector.size() - new_candidate.m_sign_nets.size()) % 2) != 0)
200  {
201  return OK(new_candidates);
202  }
203 
204  new_candidates.push_back(new_candidate);
205 
206  return OK(new_candidates);
207  }
208 
209  bool has_constant_value(const z3::expr& e, const u64& val)
210  {
211  if (!e.is_numeral())
212  {
213  return false;
214  }
215 
216  if (e.get_sort().bv_size() > 64)
217  {
218  return false;
219  }
220 
221  return e.get_numeral_uint64() == val;
222  }
223 
224  bool has_constant_value(const BooleanFunction& bf, const u64& val)
225  {
226  return bf.has_constant_value(val);
227  }
228 
230  {
231  auto input_mapping = candidate.m_control_mapping;
232 
233  for (const auto& net : candidate.m_sign_nets)
234  {
235  input_mapping.insert({net, BooleanFunction::Value::ZERO});
236  }
237 
238  const auto input_nets_res = ctx.get_variable_nets(candidate.m_output_nets[0], input_mapping);
239  if (input_nets_res.is_error())
240  {
241  return ERR_APPEND(input_nets_res.get_error(), "cannot create net pair sorting: failed to retrieve variable nets");
242  }
243  const auto input_nets = input_nets_res.get();
244 
245  if ((input_nets.size() % 2) != 0)
246  {
247  return OK({});
248  }
249 
250  FunctionalCandidate new_candidate = FunctionalCandidate(candidate);
251 
252  auto input_pair_res = create_net_pair_sorting_by_influence(ctx, new_candidate.m_output_nets[0], input_mapping);
253  if (input_pair_res.is_error())
254  {
255  return ERR_APPEND(input_pair_res.get_error(), "failed to create sorting of net pairs for LEQ");
256  }
257  const auto input_pairs = input_pair_res.get();
258 
259  if (input_pairs.empty())
260  {
261  return OK({});
262  }
263 
264  // now evaluate the function to check which is supposed bigger value
265  // set all inputs to zero
266  for (const auto& n : input_nets)
267  {
268  input_mapping.insert({n, BooleanFunction::Value::ZERO});
269  }
270 
271  //save operands to append later
272  auto to_append_operands = new_candidate.m_operands;
273  new_candidate.m_operands.clear();
274  new_candidate.m_operands.push_back({});
275  new_candidate.m_operands.push_back({});
276 
277  for (const std::pair<Net*, Net*>& cur_bit : input_pairs)
278  {
279  input_mapping[cur_bit.first] = BooleanFunction::Value::ONE;
280  bool found_first = false;
281  const auto first_res = ctx.get_boolean_function(new_candidate.m_output_nets[0], input_mapping);
282  if (first_res.is_error())
283  {
284  return ERR_APPEND(first_res.get_error(), "failed to evaluate boolean functions");
285  }
286 
287  if (has_constant_value(first_res.get(), 1))
288  {
289  found_first = true;
290  }
291  // swap and check
292  input_mapping[cur_bit.first] = BooleanFunction::Value::ZERO;
293  input_mapping[cur_bit.second] = BooleanFunction::Value::ONE;
294 
295  const auto res = ctx.get_boolean_function(new_candidate.m_output_nets[0], input_mapping);
296  if (res.is_error())
297  {
298  return ERR_APPEND(res.get_error(), "failed to evaluate boolean functions");
299  }
300 
301  input_mapping[cur_bit.second] = BooleanFunction::Value::ZERO;
302  if ((has_constant_value(res.get(), 1) && found_first) || (has_constant_value(res.get(), 0) && !found_first))
303  {
304  return OK({});
305  }
306 
307  //append newly found operands
308  if (found_first)
309  {
310  // keep order
311  new_candidate.m_operands.at(0).push_back(cur_bit.second);
312  new_candidate.m_operands.at(1).push_back(cur_bit.first);
313  }
314  else
315  {
316  // switch order if equation is true for second
317  new_candidate.m_operands.at(0).push_back(cur_bit.first);
318  new_candidate.m_operands.at(1).push_back(cur_bit.second);
319  }
320  }
321 
322  std::vector<FunctionalCandidate> result;
323  result.push_back(new_candidate);
324 
325  // create variantions for the different possible orderings of sign nets
326  for (u32 msb_idx = 1; msb_idx < candidate.m_sign_nets.size(); msb_idx++)
327  {
328  std::vector<Net*> extension_side;
329  std::vector<Net*> not_extended_side;
330 
331  for (u32 idx = 1; idx < candidate.m_sign_nets.size(); idx++)
332  {
333  if (idx == msb_idx)
334  {
335  continue;
336  }
337 
338  extension_side.push_back(candidate.m_sign_nets.at(msb_idx));
339  not_extended_side.push_back(candidate.m_sign_nets.at(idx));
340  }
341 
342  extension_side.push_back(candidate.m_sign_nets.at(msb_idx));
343  not_extended_side.push_back(candidate.m_sign_nets.at(0));
344 
345  //clone current new candidate and create two versions
346  auto new_candidate_left = FunctionalCandidate(new_candidate);
347  auto new_candidate_right = FunctionalCandidate(new_candidate);
348 
349  //do sign extension for both sides
350  new_candidate_left.m_operands[0].insert(new_candidate_left.m_operands[0].end(), extension_side.begin(), extension_side.end());
351  new_candidate_left.m_operands[1].insert(new_candidate_left.m_operands[1].end(), not_extended_side.begin(), not_extended_side.end());
352  new_candidate_right.m_operands[0].insert(new_candidate_right.m_operands[0].end(), not_extended_side.begin(), not_extended_side.end());
353  new_candidate_right.m_operands[1].insert(new_candidate_right.m_operands[1].end(), extension_side.begin(), extension_side.end());
354 
355  result.push_back(new_candidate_left);
356  result.push_back(new_candidate_right);
357  }
358 
359  return OK(result);
360  }
361 
362  // adders subtractor, counter and const mul stuff
363 
364  //helper functions
365  namespace
366  {
367  std::vector<std::vector<Net*>> combinations(const std::vector<Net*>& candidates, const u32 k)
368  {
369  std::vector<std::vector<Net*>> res;
370  std::vector<Net*> tmp;
371  std::string bitmask(k, 1); // K leading 1's
372  bitmask.resize(candidates.size(), 0); // N-K trailing 0's
373 
374  do
375  {
376  for (u32 i = 0; i < candidates.size(); ++i) // [0..N-1] integers
377  {
378  if (bitmask[i])
379  {
380  tmp.push_back(candidates[i]);
381  }
382  }
383 
384  res.push_back(tmp);
385  tmp.clear();
386  } while (std::prev_permutation(bitmask.begin(), bitmask.end()));
387  return res;
388  }
389 
390  std::map<Net*, BooleanFunction::Value> generate_control_mapping(const std::vector<Net*>& ctrl_signlas, u32 val)
391  {
392  std::map<Net*, BooleanFunction::Value> substitution_map;
393 
394  for (u32 idx = 0; idx < ctrl_signlas.size(); idx++)
395  {
396  const u32 val_i = (val >> idx) & 0x1;
397  substitution_map[ctrl_signlas.at(idx)] = val_i ? BooleanFunction::Value::ONE : BooleanFunction::ZERO;
398  }
399 
400  return substitution_map;
401  }
402 
403  std::vector<std::pair<std::map<u32, std::vector<Net*>>, std::vector<Net*>>>
404  generate_control_input_splits(const std::pair<std::map<u32, std::vector<Net*>>, std::vector<Net*>>& initial, const u32 threshold, const u32 max_control_signals, const u32 max_depth)
405  {
406  const auto& [count_to_vars, ctrl_vars] = initial;
407 
408  if (ctrl_vars.size() > max_control_signals)
409  {
410  return {};
411  }
412 
413  for (const auto& [count, vars] : count_to_vars)
414  {
415  if (vars.size() > threshold)
416  {
417  // check how many control signals have to be removed from the bin in order to fit the threshold
418  const u32 excess = vars.size() - threshold;
419  if (excess > max_control_signals)
420  {
421  return {};
422  }
423 
424  // if we already checked more bins for control signals than allowed, return an empty list
425  if (max_depth == 0)
426  {
427  return {};
428  }
429 
430  // find all possible combinations that can be removed from the bin
431  const auto combs = combinations(vars, excess);
432 
433  // for each combination build the split with the chosen control signals removed from the bin
434  std::vector<std::pair<std::map<u32, std::vector<Net*>>, std::vector<Net*>>> tmp;
435  for (const auto& c : combs)
436  {
437  auto new_result = initial;
438 
439  for (const auto& ctrl_sig : c)
440  {
441  auto& c_vars = new_result.first.at(count);
442  c_vars.erase(std::remove(c_vars.begin(), c_vars.end(), ctrl_sig), c_vars.end());
443  new_result.second.push_back(ctrl_sig);
444  }
445 
446  // TODO clean up
447  // const auto& [variable_count, ctrl_vars] = new_result;
448  tmp.push_back(new_result);
449  }
450 
451  // for each new split now check all the other bins recursively
452  std::vector<std::pair<std::map<u32, std::vector<Net*>>, std::vector<Net*>>> result;
453  for (const auto& t : tmp)
454  {
455  for (const auto& split : generate_control_input_splits(t, threshold, max_control_signals, max_depth - 1))
456  {
457  result.push_back(split);
458  }
459  }
460 
461  return result;
462  }
463  }
464 
465  return {initial};
466  }
467 
468  } // namespace
469 
471  {
472  UNUSED(ctx);
473  std::vector<FunctionalCandidate> new_candidates;
474 
475  if (candidate.m_max_control_signals < 5)
476  {
477  // determine the number of operands by finding the most common size of variable set (for a N input adder we expect most var sets to contain N variables)
478  std::map<u32, u32> size_to_occurence;
479  std::set<u32> sizes;
480  std::vector<u32> size_list;
481  for (const auto& [count, vars] : candidate.m_influence_count_to_input_nets)
482  {
483  size_list.push_back(vars.size());
484  sizes.insert(vars.size());
485  size_to_occurence[vars.size()] += 1;
486  }
487 
488  // TODO remove debug printing
489  // for (const auto& [c, nets] : candidate.m_influence_count_to_input_nets)
490  // {
491  // std::cout << c << ": " << std::endl;
492  // for (const auto& n : nets)
493  // {
494  // std::cout << "\t" << n->get_id() << " / " << n->get_name() << std::endl;
495  // }
496  // }
497 
498  std::sort(size_list.begin(), size_list.end());
499 
500  // TODO this happens for the lattice canny edge detector benchmark, need to investigate
501  if (sizes.empty())
502  {
503  log_warning("module_identification", "unable to identify control signals: found empty size list");
504  return OK({});
505  }
506 
507  const u32 upper_bound = (sizes.size() == 1) ? *(size_list.begin()) : std::min({*(size_list.rbegin()), *std::next(size_list.rbegin(), 1)});
508  u32 threshold = candidate.m_max_operands ? candidate.m_max_operands : upper_bound;
509 
510  const u32 upper_bound_alt = (sizes.size() == 2) ? *(size_list.begin()) : std::min({*std::next(size_list.rbegin(), 1), *std::next(size_list.rbegin(), 2)});
511  u32 threshold_alt = candidate.m_max_operands ? candidate.m_max_operands : upper_bound_alt;
512 
513  const u32 max_bins_including_control = 2;
514 
515  threshold = std::min({u32(6), threshold});
516  threshold_alt = std::min({u32(6), threshold_alt});
517 
518  // generate all possible combinations of nets such that in any bin are only two input vars left at max while the rest of the vars is considered control variables
519  auto control_input_splits = generate_control_input_splits({candidate.m_influence_count_to_input_nets, {}}, threshold, candidate.m_max_control_signals, max_bins_including_control);
520 
521  u32 ctrl_signals = control_input_splits.empty() ? 0 : control_input_splits.front().second.size();
522  u32 future_candidate_size = control_input_splits.size() * (1 << ctrl_signals);
523 
524  // std::cout << "Found " << ctrl_signals << " control signals and " << control_input_splits.size() << " split leading to a future size of " << future_candidate_size << " candidates."
525  // << std::endl;
526  if (future_candidate_size > 32000)
527  {
528  control_input_splits.clear();
529  }
530 
531  // TODO remove debug printing
532  // std::cout << "Found " << control_input_splits.size() << " control splits for " << threshold << " max operands and " << candidate.m_max_control_signals << " max control signals ." << std::endl;
533 
534  if (threshold_alt != threshold)
535  {
536  auto control_input_splits_alt =
537  generate_control_input_splits({candidate.m_influence_count_to_input_nets, {}}, threshold_alt, candidate.m_max_control_signals, max_bins_including_control);
538 
539  // TODO remove debug printing
540  // std::cout << "Found " << control_splits_alt.size() << " alternative control splits for " << threshold_alt << " max operands and " << candidate.m_max_control_signals
541  // << " max control signals ." << std::endl;
542 
543  u32 ctrl_signals_alt = control_input_splits_alt.empty() ? 0 : control_input_splits_alt.front().second.size();
544  u32 future_candidate_size_alt = control_input_splits_alt.size() * (1 << ctrl_signals_alt);
545 
546  // std::cout << "Found " << ctrl_signals_alt << " control signals and " << control_input_splits_alt.size() << " split leading to a future size of " << future_candidate_size_alt
547  // << " candidates." << std::endl;
548  if (future_candidate_size_alt > 32000)
549  {
550  control_input_splits_alt.clear();
551  }
552 
553  for (const auto& split : control_input_splits_alt)
554  {
555  // TODO maybe check for uniqueness
556  control_input_splits.push_back(split);
557  }
558  }
559 
560  // add input splits that take the top n bins of nets as control nets until the max control nets are reached
561  // std::vector<Net*> ctrl_vars;
562  // for (auto it = candidate.m_influence_count_to_input_nets.rbegin(); it != candidate.m_influence_count_to_input_nets.rend(); it++)
563  // {
564  // ctrl_vars.insert(ctrl_vars.end(), it->second.begin(), it->second.end());
565  // if (ctrl_vars.size() <= candidate.m_max_control_signals)
566  // {
567  // control_input_splits.push_back({{}, ctrl_vars});
568  // }
569  // else
570  // {
571  // break;
572  // }
573  // }
574 
575  if (candidate.m_influence_count_to_input_nets.rbegin()->second.size() < candidate.m_max_control_signals)
576  {
577  control_input_splits.push_back({{}, candidate.m_influence_count_to_input_nets.rbegin()->second});
578  }
579 
580  // for each split generate a new candidate
581  bool added_vanilla_candidate = false;
582  for (const auto& [_variable_count, ctrl_vars] : control_input_splits)
583  {
584  // std::cout << "Ctrl vars: " << std::endl;
585  // for (const auto& c : ctrl_vars)
586  // {
587  // std::cout << "\t" << c->get_id() << " / " << c->get_name() << std::endl;
588  // }
589 
590  auto new_candidate = FunctionalCandidate(candidate);
591 
592  // new_candidate.m_variable_count = variable_count;
593  for (const auto& cv : ctrl_vars)
594  {
595  new_candidate.m_control_signals.push_back(cv);
596  }
597 
598  new_candidates.push_back(new_candidate);
599 
600  if (ctrl_vars.empty())
601  {
602  added_vanilla_candidate = true;
603  }
604  }
605 
607  && !added_vanilla_candidate)
608  {
609  auto pass_through_candidate = FunctionalCandidate(candidate);
610  pass_through_candidate.m_control_signals.clear();
611  for (const auto& [ctrl_val, _] : pass_through_candidate.m_control_mapping)
612  {
613  pass_through_candidate.m_control_signals.push_back(ctrl_val);
614  }
615  new_candidates.push_back(pass_through_candidate);
616  }
617  }
618  else
619  {
620  log_error("module_identification", "More than 5 control signals are currently not implemented!");
621  new_candidates = {candidate};
622  }
623 
624  return OK(new_candidates);
625  }
626 
628  {
629  UNUSED(ctx);
630  std::vector<FunctionalCandidate> new_candidates;
631 
632  for (const auto& ctrl_signal : candidate.m_control_signals)
633  {
634  auto new_candidate = FunctionalCandidate(candidate);
635  new_candidate.m_control_signals.erase(std::remove(new_candidate.m_control_signals.begin(), new_candidate.m_control_signals.end(), ctrl_signal), new_candidate.m_control_signals.end());
636  new_candidate.m_ctrl_to_operand_net = ctrl_signal;
637 
638  new_candidates.push_back(new_candidate);
639  }
640 
641  return OK(new_candidates);
642  }
643 
645  {
646  UNUSED(ctx);
647  std::vector<FunctionalCandidate> new_candidates;
648 
649  const u32 max_val = 1 << candidate.m_control_signals.size();
650  for (u32 val = 0; val < max_val; val++)
651  {
652  auto new_candidate = FunctionalCandidate(candidate);
653  new_candidate.m_max_control_signals = 0;
654 
656  {
657  new_candidate.m_max_operands = module_identification::candidate_type_max_operands.at(new_candidate.m_candidate_type);
658  }
659 
660  new_candidate.m_control_mapping = generate_control_mapping(candidate.m_control_signals, val);
661 
662  new_candidates.push_back(new_candidate);
663  }
664 
665  return OK(new_candidates);
666  }
667 
668  namespace
669  {
670  std::vector<Net*> order_by_influence(const std::vector<Net*>& nets, CandidateContext& ctx, const std::map<hal::Net*, hal::BooleanFunction::Value>& ctrl_mapping)
671  {
672  std::vector<Net*> reordered_nets = nets;
673  std::map<Net*, double> output_net_to_influence_score;
674  for (const auto& n : reordered_nets)
675  {
676  const auto influences_res = ctx.get_boolean_influence(n, ctrl_mapping);
677  if (influences_res.is_error())
678  {
679  log_error(influences_res.get_error().get());
680  return {};
681  }
682  const auto& influences = influences_res.get();
683 
684  if (influences.empty())
685  {
686  // log_error("module_identification", "got empty Boolean influence for net {} with function: {}", n->get_name(), ctx.get_boolean_function(n, ctrl_mapping).to_string());
687  return {};
688  }
689 
690  double influence_score = 0;
691  for (const auto& [_, inf] : influences)
692  {
693  influence_score += inf;
694  }
695 
696  output_net_to_influence_score.insert({n, influence_score});
697  }
698 
699  std::sort(reordered_nets.begin(), reordered_nets.end(), [&output_net_to_influence_score](const auto& n1, const auto& n2) {
700  return output_net_to_influence_score.at(n1) < output_net_to_influence_score.at(n2);
701  });
702 
703  return reordered_nets;
704  }
705 
706  std::vector<std::vector<Net*>> permutations(const std::vector<Net*>& nets)
707  {
708  std::vector<Net*> initial = nets;
709  std::vector<std::vector<Net*>> result;
710 
711  std::sort(initial.begin(), initial.end());
712 
713  do
714  {
715  result.push_back({initial.begin(), initial.end()});
716  } while (std::next_permutation(initial.begin(), initial.end()));
717 
718  return result;
719  }
720 
721  std::vector<std::vector<Net*>> reorder_overfull_input_bins(const std::map<u32, std::vector<Net*>>& initial, const u32 max_overfull_bins)
722  {
723  std::vector<std::vector<Net*>> results;
724  std::vector<Net*> singleton_bins_before;
725  std::vector<Net*> singleton_bins_after;
726  std::vector<Net*> to_permute;
727  std::vector<std::vector<Net*>> overfull_bins;
728 
729  for (auto it = initial.rbegin(); it != initial.rend(); it++)
730  {
731  const auto nets = it->second;
732  if (nets.size() != 1)
733  {
734  // we allow input counts with the following pattern single* | overfull* | single*
735 
736  if (!singleton_bins_after.empty())
737  {
738  return {};
739  }
740 
741  to_permute.insert(to_permute.end(), nets.begin(), nets.end());
742  overfull_bins.push_back(nets);
743  }
744  else
745  {
746  if (to_permute.empty())
747  {
748  singleton_bins_before.push_back(nets.front());
749  }
750  else
751  {
752  singleton_bins_after.push_back(nets.front());
753  }
754  }
755  }
756 
757  // Due to computational complexity we limit the number of overfull bins to permute to 5
758  if (overfull_bins.size() > max_overfull_bins)
759  {
760  // TODO remove debug printing
761  // std::cout << "Abort, found " << overfull_bins.size() << " overfull bins." << std::endl;
762  return {};
763  }
764 
765  // Due to computational complexity we limit the number of variable to permute to 5
766  if (to_permute.size() <= 5)
767  {
768  std::sort(to_permute.begin(), to_permute.end());
769 
770  do
771  {
772  std::vector<Net*> tmp;
773  tmp.insert(tmp.end(), singleton_bins_before.begin(), singleton_bins_before.end());
774  tmp.insert(tmp.end(), to_permute.begin(), to_permute.end());
775  tmp.insert(tmp.end(), singleton_bins_after.begin(), singleton_bins_after.end());
776 
777  results.push_back(tmp);
778  } while (std::next_permutation(to_permute.begin(), to_permute.end()));
779  }
780  else
781  {
782  // check whether all overfull bins have the same size
783  std::set<u32> overfull_sizes;
784  for (const auto& bin : overfull_bins)
785  {
786  overfull_sizes.insert(bin.size());
787  }
788 
789  if (overfull_sizes.size() != 1)
790  {
791  // TODO remove debug printing
792  // std::cout << "Abort, found " << overfull_sizes.size() << " overfull bin sizes." << std::endl;
793  return {};
794  }
795 
796  const u32 bin_size = *overfull_sizes.begin();
797  if (bin_size > 5)
798  {
799  // TODO remove debug printing
800  // std::cout << "Abort, found overfull bins of size " << bin_size << "." << std::endl;
801  return {};
802  }
803 
804  // create all possible combinations of individually permuted bins
805  std::vector<std::vector<std::vector<Net*>>> isolated_permuted_bins = {{}}; // initialize with one empyt bin set
806  std::vector<std::vector<std::vector<Net*>>> new_isolated_permuted_bins;
807  for (auto bin : overfull_bins)
808  {
809  std::sort(bin.begin(), bin.end());
810  do
811  {
812  for (const auto& bin_set : isolated_permuted_bins)
813  {
814  auto new_bin_set = bin_set;
815  new_bin_set.push_back(bin);
816  new_isolated_permuted_bins.push_back(new_bin_set);
817 
818  if (new_isolated_permuted_bins.size() > 1000)
819  {
820  // TODO remove debug printing
821  // std::cout << "Abort, found too many overfull bin permutations." << std::endl;
822  return {};
823  }
824  }
825  } while (std::next_permutation(bin.begin(), bin.end()));
826 
827  isolated_permuted_bins = new_isolated_permuted_bins;
828  new_isolated_permuted_bins.clear();
829  }
830 
831  for (const auto& bin_set : isolated_permuted_bins)
832  {
833  std::vector<Net*> input_operand;
834  input_operand.insert(input_operand.end(), singleton_bins_before.begin(), singleton_bins_before.end());
835  for (u32 idx = 0; idx < bin_set.front().size(); idx++)
836  {
837  for (const auto& bin : bin_set)
838  {
839  input_operand.push_back(bin.at(idx));
840  }
841  }
842  input_operand.insert(input_operand.end(), singleton_bins_after.begin(), singleton_bins_after.end());
843  results.push_back(input_operand);
844  }
845  }
846 
847  return results;
848  }
849 
850  std::vector<std::vector<Net*>>
851  reorder_overfull_output_bins(CandidateContext& ctx, const std::map<hal::Net*, hal::BooleanFunction::Value>& ctrl_mapping, const std::map<u32, std::vector<Net*>>& initial)
852  {
853  std::vector<std::vector<Net*>> prev_results;
854  std::vector<std::vector<Net*>> next_results;
855 
856  std::vector<std::vector<Net*>> bins;
857 
858  for (auto it = initial.rbegin(); it != initial.rend(); it++)
859  {
860  bins.push_back(it->second);
861  }
862 
863  for (const auto& bin : bins)
864  {
865  // Due to computational complexity we limit the content of overfull bins to 6
866  const auto perm = (bin.size() < 4) ? permutations(bin) : std::vector<std::vector<Net*>>{order_by_influence(bin, ctx, ctrl_mapping)};
867 
868  // if the permutation is empty, we cannot create a good canidate. This can be the case for constant output functions with no influences
869  if (perm.empty())
870  {
871  return {};
872  }
873 
874  if (prev_results.empty())
875  {
876  next_results = perm;
877  }
878  else
879  {
880  for (const auto& pr : prev_results)
881  {
882  for (const auto& p : perm)
883  {
884  std::vector<Net*> tmp = pr;
885  tmp.insert(tmp.end(), p.begin(), p.end());
886  next_results.push_back(tmp);
887  }
888  }
889  }
890 
891  prev_results = next_results;
892  next_results.clear();
893  }
894 
895  return prev_results;
896  }
897  } // namespace
898 
900  {
901  auto new_candidate = FunctionalCandidate(candidate);
902 
903  if (candidate.m_output_nets.empty())
904  {
905  return OK({});
906  }
907 
908  if (candidate.m_output_nets.size() < 2)
909  {
910  return OK({});
911  }
912 
913  std::vector<std::set<hal::Net*>> variable_nets;
914  for (u32 o_idx = 0; o_idx < candidate.m_output_nets.size(); o_idx++)
915  {
916  const auto& n = candidate.m_output_nets.at(o_idx);
917  const auto variable_nets_res = ctx.get_variable_nets(n, candidate.m_control_mapping);
918  if (variable_nets_res.is_error())
919  {
920  return ERR_APPEND(variable_nets_res.get_error(),
921  "cannot update input output stats: failed to retrieve input variable nets for net " + n->get_name() + " with ID " + std::to_string(n->get_id()));
922  }
923 
924  variable_nets.push_back(variable_nets_res.get());
925  }
926 
927  // SINGLE INPUT OUTPUTS
928  std::map<Net*, Net*> single_input_to_output;
929  for (u32 o_idx = 0; o_idx < candidate.m_output_nets.size(); o_idx++)
930  {
931  const auto& n = candidate.m_output_nets.at(o_idx);
932  if (variable_nets.at(o_idx).size() == 1)
933  {
934  const auto single_net = *(variable_nets.at(o_idx).begin());
935 
936  // NOTE: currently a input net that drives two single input output nets can not create any meaningful caniddates
937  if (single_input_to_output.find(single_net) != single_input_to_output.end())
938  {
939  return OK({});
940  }
941 
942  single_input_to_output.insert({single_net, n});
943  }
944  }
945 
946  // INPUTS
947  std::map<Net*, u32> input_nets_to_influenced_outputs;
948  for (u32 o_idx = 0; o_idx < candidate.m_output_nets.size(); o_idx++)
949  {
950  for (const auto& input_net : variable_nets.at(o_idx))
951  {
952  // TODO check why we dont do this for constant multiplications
953  // ignore single inputs from the input operand count for adders and counters
956  {
957  if (single_input_to_output.find(input_net) != single_input_to_output.end())
958  {
959  continue;
960  }
961  }
962 
963  // ignore the ctrl msb for absoulte
965  {
966  if (input_net == candidate.m_ctrl_to_operand_net)
967  {
968  continue;
969  }
970  }
971 
972  input_nets_to_influenced_outputs[input_net] += 1;
973  }
974  }
975 
976  // sort input variables into bins
977  std::map<u32, std::vector<Net*>> influence_count_to_input_nets;
978  for (const auto& [var, count] : input_nets_to_influenced_outputs)
979  {
980  influence_count_to_input_nets[count].push_back(var);
981  }
982 
983  u32 max_bin_size = 0;
984  for (const auto& [_, nets] : influence_count_to_input_nets)
985  {
986  if (nets.size() > max_bin_size)
987  {
988  max_bin_size = nets.size();
989  }
990  }
991 
992  // NOTE make this a config parameter
993  if (max_bin_size > 12)
994  {
995  // log_warning("module_identification", "abandoned canidate creation for candidate {} due to a max bin size of {}", candidate.m_gates.front()->get_name(), max_bin_size);
996  return OK({});
997  }
998 
999  // OUTPUTS
1000 
1001  // count the input variables to each output function
1002  std::map<Net*, u32> output_net_to_input_count;
1003  for (u32 o_idx = 0; o_idx < candidate.m_output_nets.size(); o_idx++)
1004  {
1005  const auto& n = candidate.m_output_nets.at(o_idx);
1006  output_net_to_input_count.insert({n, u32(variable_nets.at(o_idx).size())});
1007  }
1008 
1009  std::map<u32, std::vector<Net*>> input_count_to_output_nets;
1010  for (const auto& [n, c] : output_net_to_input_count)
1011  {
1016  && c == 1)
1017  {
1018  continue;
1019  }
1020 
1021  input_count_to_output_nets[c].push_back(n);
1022  }
1023 
1024  new_candidate.m_influence_count_to_input_nets = influence_count_to_input_nets;
1025  new_candidate.m_input_count_to_output_nets = input_count_to_output_nets;
1026  new_candidate.m_single_input_to_output = single_input_to_output;
1027 
1028  return OK({new_candidate});
1029  }
1030 
1032  {
1033  UNUSED(ctx);
1034  std::vector<FunctionalCandidate> new_candidates;
1035 
1036  if (candidate.m_single_input_to_output.size() > 3)
1037  {
1038  return OK(std::vector<FunctionalCandidate>());
1039  }
1040 
1041  std::vector<std::pair<Net*, Net*>> single_input_outputs = {candidate.m_single_input_to_output.begin(), candidate.m_single_input_to_output.end()};
1042  std::sort(single_input_outputs.begin(), single_input_outputs.end());
1043 
1044  do
1045  {
1046  auto new_candidate = FunctionalCandidate(candidate);
1047  new_candidate.m_permuted_single_pairs = single_input_outputs;
1048 
1049  new_candidates.push_back(new_candidate);
1050  } while (std::next_permutation(single_input_outputs.begin(), single_input_outputs.end()));
1051 
1052  return OK(new_candidates);
1053  }
1054 
1056  {
1057  UNUSED(ctx);
1058  std::vector<FunctionalCandidate> new_candidates;
1059 
1060  // check whether all inputs are covered in the output net influenced by the most inputs
1061  u32 input_count = 0;
1062  for (const auto& [_, nets] : candidate.m_influence_count_to_input_nets)
1063  {
1064  input_count += nets.size();
1065  }
1066 
1067  if (candidate.m_input_count_to_output_nets.empty())
1068  {
1069  return OK({});
1070  }
1071 
1072  if (candidate.m_input_count_to_output_nets.rbegin()->first < input_count)
1073  {
1074  return OK({});
1075  }
1076 
1078  {
1079  // 1) Edge Case - Shifted Operand
1080  // Check whether there is a "hump" in the distribution of var count to input signals and pad the higher var counts with logical zeros to get a continously decreasing amount of variables
1081  u32 max_variables = 0;
1082  for (const auto& [_count, nets] : candidate.m_influence_count_to_input_nets)
1083  {
1084  if (nets.size() > max_variables)
1085  {
1086  max_variables = nets.size();
1087  }
1088  }
1089 
1090  // check whether the first vector of nets is smaller than the max number of nets
1091  if ((!candidate.m_influence_count_to_input_nets.empty()) && candidate.m_influence_count_to_input_nets.rbegin()->second.size() < max_variables)
1092  {
1093  bool reached_maximum_variable_count = false;
1094  std::vector<std::vector<Net*>> operands;
1095  for (auto it = candidate.m_influence_count_to_input_nets.rbegin(); it != candidate.m_influence_count_to_input_nets.rend(); it++)
1096  {
1097  const auto& [count, nets] = *it;
1098  if (nets.size() == max_variables)
1099  {
1100  reached_maximum_variable_count = true;
1101  }
1102 
1103  const u32 max_idx = reached_maximum_variable_count ? nets.size() : max_variables;
1104  for (u32 idx = 0; idx < max_idx; idx++)
1105  {
1106  if (operands.size() <= idx)
1107  {
1108  operands.push_back({});
1109  }
1110 
1111  if (idx >= nets.size())
1112  {
1113  operands.at(idx).push_back(candidate.m_gates.front()->get_netlist()->get_gnd_nets().front());
1114  }
1115  else
1116  {
1117  operands.at(idx).push_back(nets.at(idx));
1118  }
1119  }
1120  }
1121 
1122  // TODO this duplicates further below
1123  // check validity of constructed operands
1124 
1125  // The max operands is mainly here to prevent massive explosion of candidate later down the line during operand expansion
1126  bool is_valid = (operands.size() >= 2) && (operands.size() <= candidate.m_max_operands);
1127 
1128  // if one of the operands is only one bit long then this should be a counter with enable signal
1129  // NOTE there was a bug regarding the amount of non constant vaiables that i fixed. This would also be adapted in the generic candidate when merging
1130  for (const auto& op : operands)
1131  {
1132  u32 non_const_nets = 0;
1133  for (const auto& net : op)
1134  {
1135  if (!(net->is_gnd_net() || net->is_vcc_net()))
1136  {
1137  non_const_nets++;
1138  }
1139  }
1140 
1141  if (non_const_nets < 2)
1142  {
1143  is_valid = false;
1144  }
1145  }
1146 
1147  if (is_valid)
1148  {
1149  // TODO remove debug printing
1150  // std::cout << "Found the following Candidate after building inputs: " << std::endl;
1151  // for (const auto& op : operands)
1152  // {
1153  // std::cout << "OP [" << op.size() << "]: " << std::endl;
1154  // for (const auto& var : op)
1155  // {
1156  // std::cout << "\t" << var.to_string() << std::endl;
1157  // }
1158  // }
1159 
1160  auto new_candidate = FunctionalCandidate(candidate);
1161  new_candidate.m_operands = operands;
1162 
1163  new_candidates.push_back(new_candidate);
1164  }
1165  }
1166 
1167  // 2) Edge Case - Buffered Operand
1168  // find buffered signals
1169  // find variables that are just buffered signals and append them to the front of the input operand
1170 
1171  // TODO this can be moved out of the loop
1172  std::vector<std::vector<Net*>> operands;
1173 
1174  for (auto it = candidate.m_influence_count_to_input_nets.rbegin(); it != candidate.m_influence_count_to_input_nets.rend(); it++)
1175  {
1176  const auto& [count, nets] = *it;
1177  for (u32 idx = 0; idx < nets.size(); idx++)
1178  {
1179  if (candidate.m_max_operands != 0 && idx >= candidate.m_max_operands)
1180  {
1181  // the candidate would result in more than the maximum number of allowed operands
1182  return OK(std::vector<FunctionalCandidate>());
1183  }
1184 
1185  if (operands.size() <= idx)
1186  {
1187  operands.push_back({});
1188  }
1189 
1190  operands.at(idx).push_back(nets.at(idx));
1191  }
1192  }
1193 
1194  bool is_valid = (operands.size() >= 2) && (operands.size() <= candidate.m_max_operands);
1195  if (!is_valid)
1196  {
1197  return OK(std::vector<FunctionalCandidate>());
1198  }
1199 
1200  // add buffered signals to the start of one operand and pad the rest with zeros
1201  for (const auto& [in_net, out_net] : candidate.m_permuted_single_pairs)
1202  {
1203  for (u32 op_idx = 0; op_idx < operands.size(); op_idx++)
1204  {
1205  auto val = (op_idx == 0) ? in_net : candidate.m_gates.front()->get_netlist()->get_gnd_nets().front();
1206  operands.at(op_idx).insert(operands.at(op_idx).begin(), val);
1207  }
1208  }
1209 
1210  // if one of the operands is only one bit long then this should be a counter with enable signal
1211  // NOTE there was a bug regarding the amount of non constant vaiables that i fixed. This would also be adapted in the generic candidate when merging
1212  for (const auto& op : operands)
1213  {
1214  u32 non_const_nets = 0;
1215  for (const auto& net : op)
1216  {
1217  if (!(net->is_gnd_net() || net->is_vcc_net()))
1218  {
1219  non_const_nets++;
1220  }
1221  }
1222 
1223  if (non_const_nets < 2)
1224  {
1225  return OK(std::vector<FunctionalCandidate>());
1226  }
1227  }
1228 
1229  auto new_candidate = FunctionalCandidate(candidate);
1230  new_candidate.m_operands = operands;
1231 
1232  new_candidates.push_back(new_candidate);
1233  }
1235  {
1236  const auto reorderings = reorder_overfull_input_bins(candidate.m_influence_count_to_input_nets, 2);
1237  // for each split generate a new candidate
1238  for (const auto& nets : reorderings)
1239  {
1240  // ignore empty operands or operands with only one bit
1241  if (nets.empty() || nets.size() == 1)
1242  {
1243  continue;
1244  }
1245 
1246  auto new_candidate = FunctionalCandidate(candidate);
1247  new_candidate.m_operands.push_back({});
1248  for (const auto& [in_net, _] : candidate.m_permuted_single_pairs)
1249  {
1250  new_candidate.m_operands.front().push_back(in_net);
1251  }
1252 
1253  for (const auto& n : nets)
1254  {
1255  new_candidate.m_operands.front().push_back(n);
1256  }
1257 
1258  new_candidates.push_back(new_candidate);
1259  }
1260  }
1263  {
1264  // TODO remove debug printing
1265  // std::cout << "New Candidate: " << std::endl;
1266  // std::cout << candidate.get_candidate_info() << std::endl;
1267  // for (const auto& [c, nets] : candidate.m_influence_count_to_input_nets)
1268  // {
1269  // std::cout << c << ": " << std::endl;
1270  // for (const auto& n : nets)
1271  // {
1272  // std::cout << "\t" << n->get_id() << " / " << n->get_name() << std::endl;
1273  // }
1274  // }
1275 
1276  // generate all possible combinations of nets such that in any bin are only two input vars left at max while the rest of the vars is considered control variables
1277  const auto reorderings = reorder_overfull_input_bins(candidate.m_influence_count_to_input_nets, 5);
1278  // for each split generate a new candidate
1279  for (const auto& nets : reorderings)
1280  {
1281  if (nets.empty())
1282  {
1283  continue;
1284  }
1285 
1286  // check whether the permutation matches the already permuted single inputs
1287  bool found_missmatch = false;
1288  for (u32 i = 0; i < candidate.m_permuted_single_pairs.size(); i++)
1289  {
1290  if (nets.at(i) != candidate.m_permuted_single_pairs.at(i).first)
1291  {
1292  found_missmatch = true;
1293  break;
1294  }
1295  }
1296 
1297  if (found_missmatch)
1298  {
1299  continue;
1300  }
1301 
1302  auto new_candidate = FunctionalCandidate(candidate);
1303  new_candidate.m_operands.push_back({});
1304  for (const auto& n : nets)
1305  {
1306  new_candidate.m_operands.front().push_back(n);
1307  }
1308 
1309  new_candidates.push_back(new_candidate);
1310  }
1311  }
1313  {
1314  const auto reorderings = reorder_overfull_input_bins(candidate.m_influence_count_to_input_nets, 2);
1315 
1316  // for each split generate a new candidate
1317  for (const auto& nets : reorderings)
1318  {
1319  // ignore empty operands or operands with only one bit
1320  if (nets.empty() || nets.size() == 1)
1321  {
1322  continue;
1323  }
1324 
1325  auto new_candidate = FunctionalCandidate(candidate);
1326  new_candidate.m_operands.push_back({});
1327  for (const auto& [in_net, _] : candidate.m_permuted_single_pairs)
1328  {
1329  new_candidate.m_operands.front().push_back(in_net);
1330  }
1331 
1332  for (const auto& n : nets)
1333  {
1334  new_candidate.m_operands.front().push_back(n);
1335  }
1336 
1337  if (new_candidate.m_operands.front().size() >= new_candidate.m_output_nets.size())
1338  {
1339  return OK({});
1340  }
1341 
1342  if (new_candidate.m_ctrl_to_operand_net != nullptr)
1343  {
1344  new_candidate.m_operands.front().push_back(new_candidate.m_ctrl_to_operand_net);
1345  }
1346 
1347  new_candidates.push_back(new_candidate);
1348  }
1349  }
1350 
1351  // TODO remove sanity check
1352  for (const auto& nc : new_candidates)
1353  {
1354  for (const auto& net_set : nc.m_operands)
1355  {
1356  for (const auto& net : net_set)
1357  {
1358  if (net->is_gnd_net() || net->is_vcc_net())
1359  {
1360  continue;
1361  }
1362 
1363  if (const auto it = std::find(nc.m_input_nets.begin(), nc.m_input_nets.end(), net); it == nc.m_input_nets.end())
1364  {
1365  std::cout << "ERROR! Found net " << net->get_name() << " - " << net->get_id() << " in variable set that is not part of the input nets!" << std::endl;
1366  }
1367  }
1368  }
1369  }
1370 
1371  return OK(new_candidates);
1372  }
1373 
1375  {
1376  UNUSED(ctx);
1377  std::vector<FunctionalCandidate> new_candidates;
1378  auto new_candidate = FunctionalCandidate(candidate);
1379 
1380  const std::vector<std::vector<Net*>> reorderings = reorder_overfull_output_bins(ctx, candidate.m_control_mapping, new_candidate.m_input_count_to_output_nets);
1381 
1382  // for each split generate a new candidate
1383  for (const auto& nets : reorderings)
1384  {
1385  if (nets.empty())
1386  {
1387  continue;
1388  }
1389 
1390  auto reordered_candidate = FunctionalCandidate(new_candidate);
1391  reordered_candidate.m_output_nets = {nets.rbegin(), nets.rend()};
1392 
1393  new_candidates.push_back(reordered_candidate);
1394  }
1395 
1396  return OK(std::move(new_candidates));
1397  }
1398 
1400  {
1401  UNUSED(ctx);
1402  std::vector<FunctionalCandidate> new_candidates;
1403  auto new_candidate = candidate;
1404 
1405  for (auto it = candidate.m_permuted_single_pairs.rbegin(); it != candidate.m_permuted_single_pairs.rend(); it++)
1406  {
1407  const auto& [input_net, output_net] = *it;
1408  new_candidate.m_output_nets.insert(new_candidate.m_output_nets.begin(), output_net);
1409  }
1410 
1411  if ((new_candidate.m_candidate_type == module_identification::CandidateType::absolute) && (new_candidate.m_operands.at(0).size() > new_candidate.m_output_nets.size()))
1412  {
1413  return OK(std::vector<FunctionalCandidate>());
1414  }
1415 
1416  new_candidates.push_back(new_candidate);
1417 
1418  return OK(new_candidates);
1419  }
1420 
1422  {
1423  if (candidate.m_candidate_type == CandidateType::addition)
1424  {
1425  std::map<std::string, BooleanFunction::Value> zero_eval_mapping;
1426  for (const auto& n : candidate.m_input_nets)
1427  {
1428  const std::string var_name = BooleanFunctionNetDecorator(*n).get_boolean_variable_name();
1429  if (const auto it = std::find(candidate.m_control_signals.begin(), candidate.m_control_signals.end(), n); it != candidate.m_control_signals.end())
1430  {
1431  const BooleanFunction::Value val = candidate.m_control_mapping.at(n);
1432  zero_eval_mapping.insert({var_name, val});
1433  }
1434  else
1435  {
1436  zero_eval_mapping.insert({var_name, BooleanFunction::Value::ZERO});
1437  }
1438  }
1439 
1440  // set all non control inputs to zero, expect all outputs to be zero or expect all outputs to be zero except one (incase of a offset with a constant one)
1441  u32 non_zero_count = 0;
1442  for (const auto& o_net : candidate.m_output_nets)
1443  {
1444  const auto eval_res = ctx.evaluate(o_net, {}, zero_eval_mapping);
1445  if (eval_res.is_error())
1446  {
1447  return ERR_APPEND(eval_res.get_error(), "cannot check for early abort: failed to evaluate Boolean function");
1448  }
1449 
1450  if (eval_res.get().front() != BooleanFunction::Value::ZERO)
1451  {
1452  non_zero_count++;
1453  }
1454 
1455  if (non_zero_count != 0)
1456  {
1457  // early abort
1458  return OK({});
1459  }
1460  }
1461  }
1463  {
1464  // set all inputs to zero, expect all outputs to be zero
1465  std::map<std::string, BooleanFunction::Value> zero_eval_mapping;
1466  for (const auto& n : candidate.m_input_nets)
1467  {
1468  const std::string var_name = BooleanFunctionNetDecorator(*n).get_boolean_variable_name();
1469  if (const auto it = std::find(candidate.m_control_signals.begin(), candidate.m_control_signals.end(), n); it != candidate.m_control_signals.end())
1470  {
1471  const BooleanFunction::Value val = candidate.m_control_mapping.at(n);
1472  zero_eval_mapping.insert({var_name, val});
1473  }
1474  else
1475  {
1476  zero_eval_mapping.insert({var_name, BooleanFunction::Value::ZERO});
1477  }
1478  }
1479 
1480  u32 non_zero_count = 0;
1481  for (const auto& o_net : candidate.m_output_nets)
1482  {
1483  const auto eval_res = ctx.evaluate(o_net, {}, zero_eval_mapping);
1484  if (eval_res.is_error())
1485  {
1486  return ERR_APPEND(eval_res.get_error(), "cannot check for early abort: failed to evaluate Boolean function");
1487  }
1488 
1489  if (eval_res.get().front() != BooleanFunction::Value::ZERO)
1490  {
1491  non_zero_count++;
1492  }
1493 
1494  if (non_zero_count != 0)
1495  {
1496  // early abort
1497  return OK({});
1498  }
1499  }
1500  }
1502  {
1503  if (candidate.m_output_nets.size() > candidate.m_input_nets.size() + 1)
1504  {
1505  return OK({candidate});
1506  }
1507 
1508  const u32 diff = candidate.m_input_nets.size() - candidate.m_output_nets.size();
1509  if (diff >= candidate.m_output_nets.size())
1510  {
1511  return OK({});
1512  }
1513  }
1514 
1515  return OK({candidate});
1516  }
1517 
1518  namespace
1519  {
1520  std::vector<std::vector<std::vector<Net*>>> generate_operand_permutations(const std::vector<std::vector<Net*>>& operands, const u32 bit_position)
1521  {
1522  std::vector<Net*> sign_bit_signals;
1523  std::vector<u32> op_indices;
1524 
1525  for (u32 op_idx = 0; op_idx < operands.size(); op_idx++)
1526  {
1527  const auto& op = operands.at(op_idx);
1528  if (bit_position < op.size())
1529  {
1530  sign_bit_signals.push_back(op.at(bit_position));
1531  op_indices.push_back(op_idx);
1532  }
1533  }
1534 
1535  if (op_indices.size() == 1)
1536  {
1537  return {operands};
1538  }
1539 
1540  std::vector<std::vector<std::vector<Net*>>> permuted_operands;
1541 
1542  do
1543  {
1544  std::vector<std::vector<Net*>> ops = operands;
1545 
1546  for (u32 i = 0; i < op_indices.size(); i++)
1547  {
1548  const auto op_idx = op_indices.at(i);
1549 
1550  ops.at(op_idx).at(bit_position) = sign_bit_signals.at(i);
1551  }
1552 
1553  permuted_operands.push_back(ops);
1554 
1555  } while (std::next_permutation(op_indices.begin(), op_indices.end()));
1556 
1557  // std::cout << "Found " << op_indices.size() << " operands that contain bit position " << bit_position << std::endl;
1558  // std::cout << "Generated " << permuted_operands.size() << " permuted varianst." << std::endl;
1559 
1560  return permuted_operands;
1561  }
1562  } // namespace
1563 
1564  namespace
1565  {
1566  std::vector<Net*> sign_extend_operand(const std::vector<Net*>& operand, const u32 new_size, Net* sign_net = nullptr)
1567  {
1568  std::vector<Net*> new_operand;
1569 
1570  auto sn = sign_net == nullptr ? operand.back() : sign_net;
1571 
1572  for (u32 idx = 0; idx < new_size; idx++)
1573  {
1574  if (idx < operand.size())
1575  {
1576  new_operand.push_back(operand.at(idx));
1577  }
1578  else
1579  {
1580  new_operand.push_back(sn);
1581  }
1582  }
1583 
1584  return new_operand;
1585  }
1586 
1587  std::vector<Net*> zero_extend_operand(const std::vector<Net*>& operand, const u32 new_size, const Netlist* nl)
1588  {
1589  std::vector<Net*> new_operand;
1590 
1591  for (u32 idx = 0; idx < new_size; idx++)
1592  {
1593  if (idx < operand.size())
1594  {
1595  new_operand.push_back(operand.at(idx));
1596  }
1597  else
1598  {
1599  new_operand.push_back(nl->get_gnd_nets().front());
1600  }
1601  }
1602 
1603  return new_operand;
1604  }
1605 
1606  std::vector<Net*> apply_extension(const std::vector<Net*>& op, const u32 size, const u32 extension_type, Net* sign_net, const Netlist* nl)
1607  {
1608  std::vector<Net*> new_op = op;
1609 
1610  switch (extension_type)
1611  {
1612  case 0:
1613  // zero extended
1614  new_op = zero_extend_operand(new_op, size, nl);
1615  break;
1616  case 1:
1617  // sign extended
1618  new_op = sign_extend_operand(new_op, size);
1619  break;
1620  case 2:
1621  // sign extended up until the second highest bit
1622  new_op = sign_extend_operand(new_op, size - 1, sign_net);
1623  new_op = zero_extend_operand(new_op, size, nl);
1624  break;
1625  case 3:
1626  if ((op.size() == size) && (op.back() == sign_net))
1627  {
1628  new_op = op;
1629  }
1630  else
1631  {
1632  new_op = zero_extend_operand(new_op, size, nl);
1633  }
1634  }
1635 
1636  return new_op;
1637  }
1638  } // namespace
1639 
1641  {
1642  UNUSED(ctx);
1643  std::vector<FunctionalCandidate> new_candidates;
1644 
1645  std::set<u32> sign_bit_positions;
1646  for (const auto& op : candidate.m_operands)
1647  {
1648  sign_bit_positions.insert(op.size() - 1);
1649  }
1650 
1651  std::vector<std::vector<std::vector<Net*>>> permuted_operands = {candidate.m_operands};
1652 
1653  for (const auto& sbp : sign_bit_positions)
1654  {
1655  std::vector<std::vector<std::vector<Net*>>> new_permuted_operands;
1656  for (const auto& permuted_op : permuted_operands)
1657  {
1658  const auto new_temp = generate_operand_permutations(permuted_op, sbp);
1659  new_permuted_operands.insert(new_permuted_operands.end(), new_temp.begin(), new_temp.end());
1660  }
1661  permuted_operands = new_permuted_operands;
1662  }
1663 
1664  for (const auto& op_set : permuted_operands)
1665  {
1666  auto permuted_candidate = FunctionalCandidate(candidate);
1667  permuted_candidate.m_operands = op_set;
1668  new_candidates.push_back(permuted_candidate);
1669  }
1670 
1671  return OK(new_candidates);
1672  }
1673 
1674  namespace
1675  {
1676  std::vector<std::vector<u32>> combinations_with_repetittions(const std::vector<u32>& v, const std::vector<u32>& stack, const u32 k)
1677  {
1678  if (k == 0)
1679  {
1680  return {stack};
1681  }
1682 
1683  std::vector<std::vector<u32>> result;
1684  for (u32 i = 0; i < v.size(); i++)
1685  {
1686  auto new_stack = stack;
1687  new_stack.push_back(v.at(i));
1688 
1689  const auto new_combinations = combinations_with_repetittions(v, new_stack, k - 1);
1690  result.insert(result.end(), new_combinations.begin(), new_combinations.end());
1691  }
1692 
1693  return result;
1694  }
1695  } // namespace
1696 
1698  {
1699  UNUSED(ctx);
1700  std::vector<FunctionalCandidate> new_candidates;
1701 
1702  std::vector<u32> possible_extensions;
1705  {
1706  possible_extensions = {0, 1, 2};
1707  }
1708  else if (candidate.m_operands.size() == 1)
1709  {
1710  if (candidate.m_operands.front().size() < (candidate.m_output_nets.size() - 1))
1711  {
1712  possible_extensions = {0, 1, 2};
1713  }
1714  else if (candidate.m_operands.front().size() < candidate.m_output_nets.size())
1715  {
1716  possible_extensions = {0, 1};
1717  }
1718  else
1719  {
1720  possible_extensions = {};
1721  }
1722  }
1723  else
1724  {
1725  possible_extensions = {0, 1, 2};
1726  }
1727 
1728  std::vector<std::vector<u32>> extension_sets;
1729 
1732  {
1733  extension_sets = combinations_with_repetittions(possible_extensions, {}, candidate.m_operands.size());
1734  }
1735  else
1736  {
1737  for (const auto& pe : possible_extensions)
1738  {
1739  extension_sets.push_back(std::vector<u32>(candidate.m_operands.size(), pe));
1740  }
1741  }
1742 
1743  if (extension_sets.empty())
1744  {
1745  const auto new_candidate = FunctionalCandidate(candidate);
1746  return OK({new_candidate});
1747  }
1748 
1749  std::set<u32> operand_lengths;
1750  for (const auto& op : candidate.m_operands)
1751  {
1752  operand_lengths.insert(op.size());
1753  }
1754 
1755  // NOTE: this is currently disabled because in the case that the lower bits of the outputs are cut off we need other ways to reconstruct a bitorder for the input operand as well.
1756  // if the amount of outputs is shorter than the largest input operand we add more possible output lengths. In the check we then extract only a Slice with the size of the original output
1757  const std::set<u32> possible_output_sizes = {
1758  u32(candidate.m_output_nets.size()),
1759  // std::max(max_operand_length, u32(candidate.m_output_nets.size())),
1760  // std::max(max_operand_length + 1, u32(candidate.m_output_nets.size())),
1761  };
1762 
1763  for (const auto& ex_s : extension_sets)
1764  {
1765  for (const auto& out_size : possible_output_sizes)
1766  {
1767  auto new_candidate = FunctionalCandidate(candidate);
1768  for (u32 op_idx = 0; op_idx < new_candidate.m_operands.size(); op_idx++)
1769  {
1772  {
1773  // new_candidate.m_operands.at(op_idx) = apply_extension_const_mul(new_candidate.m_operands.at(op_idx), new_candidate.m_output_nets.size(), ex_s.at(op_idx), msb);
1774  auto sign_net = candidate.m_operands.front().back();
1775  new_candidate.m_operands.at(op_idx) = apply_extension(new_candidate.m_operands.at(op_idx), out_size, ex_s.at(op_idx), sign_net, ctx.m_netlist);
1776  }
1777  else
1778  {
1779  auto sign_net = new_candidate.m_operands.at(op_idx).back();
1780  new_candidate.m_operands.at(op_idx) = apply_extension(new_candidate.m_operands.at(op_idx), out_size, ex_s.at(op_idx), sign_net, ctx.m_netlist);
1781  }
1782  }
1783 
1784  // check for uniqueness to avoid duplicates
1785  bool is_unique = true;
1786  for (const auto& nc : new_candidates)
1787  {
1788  if (nc.m_operands == new_candidate.m_operands)
1789  {
1790  is_unique = false;
1791  break;
1792  }
1793  }
1794 
1795  if (is_unique)
1796  {
1797  new_candidates.push_back(new_candidate);
1798  }
1799  }
1800  }
1801 
1802  return OK(new_candidates);
1803  }
1804 
1806  {
1807  UNUSED(ctx);
1808  std::vector<FunctionalCandidate> new_candidates;
1809  new_candidates.emplace_back(candidate);
1810 
1811  const auto extended_output_nets = get_output_nets(candidate.m_gates, false);
1812  if (candidate.m_output_nets.size() != extended_output_nets.size())
1813  {
1814  const u32 difference = extended_output_nets.size() - candidate.m_output_nets.size();
1815  const u32 threshold = 3;
1816  if (difference <= threshold)
1817  {
1818  auto new_candidate = FunctionalCandidate(candidate);
1819  new_candidate.m_output_nets = extended_output_nets;
1820 
1821  new_candidates.push_back(std::move(new_candidate));
1822  }
1823  }
1824 
1825  return OK(new_candidates);
1826  }
1827 
1828  //value check
1829 
1831  {
1832  UNUSED(ctx);
1833  std::vector<FunctionalCandidate> new_candidates;
1834 
1835  FunctionalCandidate new_candidate = {candidate};
1836 
1837  if (candidate.m_output_nets.size() == 1)
1838  {
1839  new_candidates.emplace_back(new_candidate);
1840  }
1841  else
1842  {
1843  // check for output nets that only lead to outside gates
1844  std::vector<Net*> filtered_output_nets = get_output_nets(new_candidate.m_gates, true);
1845 
1846  if (filtered_output_nets.size() == 1)
1847  {
1848  new_candidate.m_output_nets = filtered_output_nets;
1849  new_candidates.push_back(new_candidate);
1850  }
1851  }
1852 
1853  return OK(new_candidates);
1854  }
1855 
1857  {
1858  UNUSED(ctx);
1859  std::vector<FunctionalCandidate> new_candidates;
1860 
1861  FunctionalCandidate new_candidate = {candidate};
1862 
1863  new_candidate.m_operands.push_back({});
1864  for (const auto& n : new_candidate.m_input_nets)
1865  {
1866  new_candidate.m_operands.front().push_back(n);
1867  }
1868 
1869  new_candidates.emplace_back(new_candidate);
1870 
1871  return OK(new_candidates);
1872  }
1873 
1874  // constant multiplication
1875 
1876  FunctionalCandidate FunctionalCandidate::add_n_shifted_operands(const FunctionalCandidate& candidate, const std::vector<i32>& shift_vals)
1877  {
1878  auto new_operands = candidate.m_operands;
1879 
1880  for (const auto& n : shift_vals)
1881  {
1882  // add n shifted operand
1883  if (n < 0)
1884  {
1885  // shift right
1886  const u32 n_abs = abs(n);
1887  if (n_abs < candidate.m_operands.at(0).size())
1888  {
1889  // clone and shift input operand
1890  std::vector<Net*> new_operand;
1891  for (u32 idx = 0; idx < new_operands.at(0).size() - n_abs; idx++)
1892  {
1893  new_operand.push_back(new_operands.at(0).at(idx + n_abs));
1894  }
1895  new_operands.push_back(new_operand);
1896  }
1897  }
1898  else
1899  {
1900  // shift left
1901  std::vector<Net*> new_operand;
1902  for (u32 i = 0; i < (u32)n; i++)
1903  {
1904  new_operand.push_back(candidate.m_gates.front()->get_netlist()->get_gnd_nets().front());
1905  }
1906  for (const auto& net : new_operands.at(0))
1907  {
1908  new_operand.push_back(net);
1909  }
1910  new_operands.push_back(new_operand);
1911  }
1912  }
1913 
1914  auto new_candidate = FunctionalCandidate(candidate);
1915  new_candidate.m_operands = new_operands;
1916 
1917  return new_candidate;
1918  }
1919 
1921  {
1922  UNUSED(ctx);
1923 
1924  std::vector<FunctionalCandidate> new_candidates;
1925 
1926  // static const std::map<std::vector<std::vector<u32>>, std::vector<std::vector<i32>>> finger_print_library = {
1927  // {{{0, 4, 5}, {1, 6}, {2, 7}}, {{-5, -4}}},
1928  // {{{0, 3, 5}, {1, 4, 6}, {2, 7}}, {{-5, -3}}},
1929  // {{{0, 2, 5}, {1, 3, 6}, {4, 7}}, {{-5, -2}}},
1930  // {{{0, 1, 5}, {2, 6}, {3, 7}}, {{-5, -1}}},
1931  // {{{0, 5}, {1, 6}, {2, 7}}, {{-5}, {-5, 1}, {-5, 2}, {-5, 3}, {-5, 4}, {-5, 5}, {5}}},
1932  // {{{0, 3, 4}, {1, 5}, {2, 6}}, {{-4, -3}}},
1933  // {{{0, 2, 4}, {1, 3, 5}, {6}}, {{-4, -2}}},
1934  // {{{0, 1, 4}, {2, 5}, {3, 6}}, {{-4, -1}}},
1935  // {{{0, 4}, {1, 5}, {2, 6}}, {{-4}, {-4, 1}, {-4, 2}, {-4, 3}, {-4, 4}, {-4, 5}, {4}, {4, 5}}},
1936  // {{{0, 2, 3}, {1, 4}, {5}}, {{-3, -2}}},
1937  // {{{0, 1, 3}, {2, 4}, {5}}, {{-3, -1}}},
1938  // {{{0, 3}, {1, 4}, {2, 5}}, {{-3}, {-3, 1}, {-3, 2}, {-3, 3}, {-3, 4}, {-3, 5}, {3}, {3, 4}, {3, 5}}},
1939  // {{{0, 1, 2}, {3}, {4}}, {{-2, -1}}},
1940  // {{{0, 2}, {1, 3}, {4}}, {{-2}, {-2, 1}, {-2, 2}, {-2, 3}, {-2, 4}, {-2, 5}, {2}, {2, 3}, {2, 4}, {2, 5}}},
1941  // {{{0, 1}, {2}, {3}}, {{-1}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {1}, {1, 2}, {1, 3}, {1, 4}, {1, 5}}}};
1942 
1943  static const std::map<std::vector<std::vector<u32>>, std::vector<std::vector<i32>>> finger_print_library = {{{{0, 4, 5}, {1, 6}, {2, 7}}, {{-5, -4}}},
1944  {{{0, 3, 5}, {1, 4, 6}, {2, 7}}, {{-5, -3}}},
1945  {{{0, 2, 5}, {1, 3, 6}, {4, 7}}, {{-5, -2}}},
1946  {{{0, 1, 5}, {2, 6}, {3, 7}}, {{-5, -1}}},
1947  {{{0, 5}, {1, 6}, {2, 7}}, {{-5}, {-5, 1}, {-5, 2}, {-5, 3}, {-5, 4}, {-5, 5}}},
1948  {{{0, 3, 4}, {1, 5}, {2, 6}}, {{-4, -3}}},
1949  {{{0, 2, 4}, {1, 3, 5}, {6}}, {{-4, -2}}},
1950  {{{0, 1, 4}, {2, 5}, {3, 6}}, {{-4, -1}}},
1951  {{{0, 4}, {1, 5}, {2, 6}}, {{-4}, {-4, 1}, {-4, 2}, {-4, 3}, {-4, 4}, {-4, 5}}},
1952  {{{0, 2, 3}, {1, 4}, {5}}, {{-3, -2}}},
1953  {{{0, 1, 3}, {2, 4}, {5}}, {{-3, -1}}},
1954  {{{0, 3}, {1, 4}, {2, 5}}, {{-3}, {-3, 1}, {-3, 2}, {-3, 3}, {-3, 4}, {-3, 5}}},
1955  {{{0, 1, 2}, {3}, {4}}, {{-2, -1}}},
1956  {{{0, 2}, {1, 3}, {4}}, {{-2}, {-2, 1}, {-2, 2}, {-2, 3}, {-2, 4}, {-2, 5}}},
1957  {{{0, 1}, {2}, {3}}, {{-1}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}}},
1958  // {{{0}, {1}, {2}}, {{1}, {1, 2}, {1, 3}, {1, 4}, {1, 5}}},
1959  {{{0}, {1, 2}, {3}}, {{2}, {2, 3}, {2, 4}, {2, 5}}},
1960  {{{0}, {1, 3}, {2, 4}}, {{3}, {3, 4}, {3, 5}}},
1961  {{{0}, {1, 4}, {2, 5}}, {{4}, {4, 5}}},
1962  {{{0}, {1, 5}, {2, 6}}, {{5}}}};
1963 
1964  // generate new influence_count with single inputs ignored
1965  std::vector<std::set<hal::Net*>> variable_nets;
1966  for (u32 o_idx = 0; o_idx < candidate.m_output_nets.size(); o_idx++)
1967  {
1968  const auto& n = candidate.m_output_nets.at(o_idx);
1969  const auto variable_nets_res = ctx.get_variable_nets(n, candidate.m_control_mapping);
1970  if (variable_nets_res.is_error())
1971  {
1972  return ERR_APPEND(variable_nets_res.get_error(),
1973  "cannot update input output stats: failed to retrieve input variable nets for net " + n->get_name() + " with ID " + std::to_string(n->get_id()));
1974  }
1975 
1976  variable_nets.push_back(variable_nets_res.get());
1977  }
1978 
1979  // INPUTS
1980  std::map<Net*, u32> input_nets_to_influenced_outputs;
1981  for (u32 o_idx = 0; o_idx < candidate.m_output_nets.size(); o_idx++)
1982  {
1983  if (variable_nets.size() <= 1)
1984  {
1985  continue;
1986  }
1987 
1988  for (const auto& input_net : variable_nets.at(o_idx))
1989  {
1990  input_nets_to_influenced_outputs[input_net] += 1;
1991  }
1992  }
1993 
1994  // sort input variables into bins
1995  std::map<u32, std::vector<Net*>> influence_count_to_input_nets;
1996  for (const auto& [var, count] : input_nets_to_influenced_outputs)
1997  {
1998  influence_count_to_input_nets[count].push_back(var);
1999  }
2000 
2001  if (influence_count_to_input_nets.size() < 3)
2002  {
2003  return OK(new_candidates);
2004  }
2005 
2006  // the finger print of a constant multiplication are the 3 bins of input nets with the highest amount of output bits influenced.
2007  // the bins are translated into the corresponding net indices inside the input operands
2008  std::vector<std::vector<u32>> finger_print;
2009  auto rit = influence_count_to_input_nets.rbegin();
2010  for (u32 i = 0; i < 3; i++, rit++)
2011  {
2012  const auto& [cnt, nets] = *rit;
2013  // this is a vector representing the net indices/position in the input operand of this candidate
2014  std::vector<u32> net_indices;
2015  for (const auto& n : nets)
2016  {
2017  const auto& op = candidate.m_operands.front();
2018  const auto f_it = std::find(op.begin(), op.end(), n);
2019  if (f_it == op.end())
2020  {
2021  // TODO check why this happens
2022  return OK({});
2023  }
2024  const u32 index = f_it - op.begin();
2025  net_indices.push_back(index);
2026  }
2027 
2028  std::sort(net_indices.begin(), net_indices.end());
2029  finger_print.push_back(net_indices);
2030  }
2031 
2032  // TODO remove debug printing
2033  // for (const auto& [c, nets] : candidate.m_influence_count_to_input_nets)
2034  // {
2035  // std::cout << c << ": " << std::endl;
2036  // for (const auto& n : nets)
2037  // {
2038  // std::cout << "\t" << n->get_id() << " / " << n->get_name() << std::endl;
2039  // }
2040  // }
2041 
2042  // std::cout << "Found Fingerprint: " << std::endl;
2043  // for (const auto& v : finger_print)
2044  // {
2045  // std::cout << "{" << std::endl;
2046  // for (const auto& n : v)
2047  // {
2048  // std::cout << "\t" << n << " / " << candidate.m_operands.front().at(n)->get_name() << std::endl;
2049  // }
2050  // std::cout << "}" << std::endl;
2051  // }
2052 
2053  if (const auto fpl_it = finger_print_library.find(finger_print); fpl_it != finger_print_library.end())
2054  {
2055  for (const auto& offsets : fpl_it->second)
2056  {
2057  auto new_candidate_nm = add_n_shifted_operands(candidate, offsets);
2058  new_candidate_nm.add_additional_data("OPERAND_SHIFTS", utils::join(", ", offsets));
2059  new_candidates.push_back(new_candidate_nm);
2060  }
2061  }
2062 
2063  return OK(new_candidates);
2064  }
2065 
2067  {
2068  UNUSED(ctx);
2069 
2070  std::vector<FunctionalCandidate> new_candidates;
2071 
2072  // static const std::vector<std::vector<i32>> all_possible_offsets = {
2073  // {-5, -4}, {-5, -3}, {-5, -2}, {-5, -1}, {-5}, {-5, 1}, {-5, 2}, {-5, 3}, {-5, 4}, {-5, 5}, {-4, -3}, {-4, -2}, {-4, -1}, {-4}, {-4, 1}, {-4, 2}, {-4, 3}, {-4, 4}, {-4, 5},
2074  // {-3, -2}, {-3, -1}, {-3}, {-3, 1}, {-3, 2}, {-3, 3}, {-3, 4}, {-3, 5}, {-2, -1}, {-2}, {-2, 1}, {-2, 2}, {-2, 3}, {-2, 4}, {-2, 5}, {-1}, {-1, 1}, {-1, 2}, {-1, 3},
2075  // {-1, 4}, {-1, 5}, {1}, {1, 2}, {1, 3}, {1, 4}, {1, 5}, {2}, {2, 3}, {2, 4}, {2, 5}, {3}, {3, 4}, {3, 5}, {4}, {4, 5}, {5}};
2076 
2077  static const std::vector<std::vector<i32>> all_possible_offsets = {{1}, {2}, {3}, {-2}, {-3}};
2078 
2079  for (const auto& offsets : all_possible_offsets)
2080  {
2081  auto new_candidate_nm = add_n_shifted_operands(candidate, offsets);
2082  new_candidate_nm.add_additional_data("OPERAND_SHIFTS", utils::join(", ", offsets));
2083  new_candidates.push_back(new_candidate_nm);
2084  }
2085 
2086  return OK(new_candidates);
2087  }
2088 
2090  u32 max_control_signal,
2091  CandidateContext& ctx,
2092  module_identification::CandidateType candidate_type,
2093  const std::vector<std::vector<Gate*>>& registers)
2094  {
2095  auto gates = sc->m_gates;
2096  std::vector<FunctionalCandidate> candidates;
2097 
2098  candidates.push_back(FunctionalCandidate(sc, max_control_signal, candidate_type));
2099 
2100  std::vector<std::function<Result<std::vector<FunctionalCandidate>>(CandidateContext & ctx, const FunctionalCandidate&)>> operations;
2101  switch (candidate_type)
2102  {
2104  operations = {
2107  };
2108  break;
2110  operations = {
2117  };
2118  break;
2120  operations = {
2125  early_abort,
2133  [registers](CandidateContext& c_ctx, const FunctionalCandidate& fc) -> Result<std::vector<FunctionalCandidate>> {
2134  UNUSED(c_ctx);
2135  const auto reordered_operands = reorder_commutative_operands(fc.m_operands, registers);
2136  auto new_candidate = FunctionalCandidate(fc);
2137  new_candidate.m_operands = reordered_operands;
2138  return OK({fc, new_candidate});
2139  },
2140  };
2141  break;
2143  operations = {
2155  [registers](CandidateContext& c_ctx, const FunctionalCandidate& fc) -> Result<std::vector<FunctionalCandidate>> {
2156  UNUSED(c_ctx);
2157  const auto reordered_operands = reorder_commutative_operands(fc.m_operands, registers);
2158  auto new_candidate = FunctionalCandidate(fc);
2159  new_candidate.m_operands = reordered_operands;
2160  return OK({fc, new_candidate});
2161  },
2162  };
2163  break;
2165  operations = {
2168  };
2169  break;
2171  operations = {
2175  early_abort,
2183  };
2184  break;
2186  operations = {
2197  };
2198  break;
2200  operations = {
2202  early_abort,
2212  };
2213  break;
2215  operations = {
2217  early_abort,
2228 
2229  };
2230  break;
2231  default:
2232  return ERR("no preprocessing available for candidate type" + enum_to_string(candidate_type));
2233  }
2234 
2235  // #define PRINT_OPS
2236 
2237 #ifdef PRINT_OPS
2238  std::cout << "------------------------" << std::endl;
2239  std::cout << "Building candidates for carry " << gates.front()->get_name() << " and type: " << candidate_type << std::endl;
2240  std::cout << "------------------------" << std::endl;
2241 #endif
2242 
2243  for (u32 op_idx = 0; op_idx < operations.size(); op_idx++)
2244  {
2245 #ifdef PRINT_OPS
2246  std::cout << "------------------------" << std::endl;
2247  std::cout << "Excecuting operation " << sc->m_gates.front()->get_name() << " / " << sc->m_gates.front()->get_id() << ": " << op_idx << std::endl;
2248  std::cout << "------------------------" << std::endl;
2249 #endif
2250  std::vector<FunctionalCandidate> new_candidates;
2251  for (const auto& current_candidate : candidates)
2252  {
2253  auto resulting_candidates_res = operations.at(op_idx)(ctx, current_candidate);
2254  if (resulting_candidates_res.is_error())
2255  {
2256  return ERR_APPEND(resulting_candidates_res.get_error(), ("failed functional candidate creation in operation with index " + std::to_string(op_idx)));
2257  }
2258  auto resulting_candidates = resulting_candidates_res.get();
2259 
2260  for (auto& rc : resulting_candidates)
2261  {
2262  new_candidates.push_back(rc);
2263  }
2264  }
2265 
2266 #ifdef PRINT_OPS
2267  std::cout << "------------------------" << std::endl;
2268  std::cout << "New Candidates: " << sc->m_gates.front()->get_name() << " / " << sc->m_gates.front()->get_id() << ": " << new_candidates.size() << std::endl;
2269  std::cout << "------------------------" << std::endl;
2270 #endif
2271 
2272  candidates = new_candidates;
2273 
2274  if (candidates.empty())
2275  {
2276  break;
2277  }
2278  }
2279 
2280 #ifdef PRINT_OPS
2281  std::cout << "------------------------" << std::endl;
2282  std::cout << "Candidates for type " << sc->m_gates.front()->get_name() << " / " << sc->m_gates.front()->get_id() << ": " << candidate_type << std::endl;
2283  std::cout << "Total Candidates: " << candidates.size() << std::endl;
2284  std::cout << "------------------------" << std::endl;
2285 #endif
2286 
2287  return OK(candidates);
2288  }
2289  } // namespace module_identification
2290 } // namespace hal
This file contains the definition of the BaseCandidate class, which represents a base candidate in th...
u32 size
This file contains the enumeration and constants for the candidate types used in the module identific...
bool has_constant_value(const std::vector< Value > &value) const
Value
represents the type of the node
static Result< Net * > get_net_from(const Netlist *netlist, const BooleanFunction &var)
Definition: net.h:58
Represents a functional candidate derived from structural candidates.
static hal::Result< std::vector< FunctionalCandidate > > discard_equal_candidate(CandidateContext &ctx, const FunctionalCandidate &candidate)
Discard equal candidates based on their number of input signals.
static hal::Result< std::vector< FunctionalCandidate > > create_input_extension_variants(CandidateContext &ctx, const FunctionalCandidate &candidate)
Create input extension variants for an adder, subtractor, or counter.
std::vector< std::pair< Net *, Net * > > m_permuted_single_pairs
static hal::Result< std::vector< FunctionalCandidate > > build_input_operand(CandidateContext &ctx, const FunctionalCandidate &candidate)
Build an input operand for a functional candidate by including all external input signals of the cand...
static hal::Result< std::vector< FunctionalCandidate > > add_single_input_signals(CandidateContext &ctx, const FunctionalCandidate &candidate)
Add single input signals for an adder, subtractor, or counter.
static hal::Result< std::vector< FunctionalCandidate > > add_selected_shifted_operand(CandidateContext &ctx, const FunctionalCandidate &candidate)
Add shifted operands to the functional candidate based on its input output stats.
static hal::Result< std::vector< FunctionalCandidate > > trim_to_single_output_net(CandidateContext &ctx, const FunctionalCandidate &candidate)
Trim a functional candidate to a single output net.
std::map< u32, std::vector< Net * > > m_influence_count_to_input_nets
static hal::Result< std::vector< FunctionalCandidate > > add_all_shifted_operand(CandidateContext &ctx, const FunctionalCandidate &candidate)
Add all possible variations of possible shifted operands to the functional candidate.
static hal::Result< std::vector< FunctionalCandidate > > realize_control_signals(CandidateContext &ctx, const FunctionalCandidate &candidate)
Realize control signals for an adder, subtractor, or counter by setting the control signals to concre...
static hal::Result< std::vector< FunctionalCandidate > > permute_single_input_signals(CandidateContext &ctx, const FunctionalCandidate &candidate)
Permute single input signals for an adder, subtractor, or counter.
static hal::Result< std::vector< FunctionalCandidate > > create_sign_bit_variants(CandidateContext &ctx, const FunctionalCandidate &candidate)
Create sign bit variants for an adder, subtractor, or counter.
static hal::Result< std::vector< FunctionalCandidate > > check_output_size(CandidateContext &ctx, const FunctionalCandidate &candidate)
Check the output size of a functional candidate.
static hal::Result< std::vector< FunctionalCandidate > > find_control_signals(CandidateContext &ctx, const FunctionalCandidate &candidate)
Find control signals for a functional candidate.
static hal::Result< std::vector< FunctionalCandidate > > update_input_output_stats(CandidateContext &ctx, const FunctionalCandidate &candidate)
Update input and output statistics for an adder, subtractor, or counter.
static hal::Result< std::vector< FunctionalCandidate > > create_sign_extension_variants(CandidateContext &ctx, const FunctionalCandidate &candidate)
Create sign extension variants of a functional candidate.
static hal::Result< std::vector< FunctionalCandidate > > create_candidates(StructuralCandidate *sc, u32 max_control_signal, CandidateContext &ctx, module_identification::CandidateType candidate_type, const std::vector< std::vector< Gate * >> &registers)
Create functional candidates from a structural candidate.
static hal::Result< std::vector< FunctionalCandidate > > identify_control_signals(CandidateContext &ctx, const FunctionalCandidate &candidate)
Identify control signals for an adder, subtractor, or counter.
std::map< u32, std::vector< Net * > > m_input_count_to_output_nets
static hal::Result< std::vector< FunctionalCandidate > > order_input_operands(CandidateContext &ctx, const FunctionalCandidate &candidate)
Order input operands for a functional candidate.
static FunctionalCandidate add_n_shifted_operands(const FunctionalCandidate &candidate, const std::vector< i32 > &shift_vals)
Add n shifted operands for constant multiplication.
static hal::Result< std::vector< FunctionalCandidate > > order_output_signals(CandidateContext &ctx, const FunctionalCandidate &candidate)
Order output signals for an adder, subtractor, or counter.
std::map< Net *, BooleanFunction::Value > m_control_mapping
static hal::Result< std::vector< FunctionalCandidate > > build_input_operands(CandidateContext &ctx, const FunctionalCandidate &candidate)
Build input operands for an adder, subtractor, or counter.
static hal::Result< std::vector< FunctionalCandidate > > early_abort(CandidateContext &ctx, const FunctionalCandidate &candidate)
Early abort process for a functional candidate.
module_identification::CandidateType m_candidate_type
static hal::Result< std::vector< FunctionalCandidate > > create_operand_control_variations(CandidateContext &ctx, const FunctionalCandidate &candidate)
Create operand control variations for absolute functional candidates.
FunctionalCandidate(StructuralCandidate *sc, u32 max_control_signal, module_identification::CandidateType candidate_type)
Constructor for FunctionalCandidate.
static hal::Result< std::vector< FunctionalCandidate > > create_output_net_variant(CandidateContext &ctx, const FunctionalCandidate &candidate)
Create output net variants for an adder, subtractor, or counter.
std::vector< std::vector< Net * > > m_operands
A class representing a structural candidate for module identification.
std::vector< Gate * > m_gates
Vector of gates that form the structural candidate.
uint64_t u64
Definition: defines.h:42
uint32_t u32
Definition: defines.h:41
#define UNUSED(expr)
Definition: defines.h:49
This file contains the class and functions for handling functional candidates within the module ident...
#define log_error(channel,...)
Definition: log.h:78
#define log_warning(channel,...)
Definition: log.h:76
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
#define ERR_APPEND(prev_error, message)
Definition: result.h:64
void remove(std::filesystem::path file_path)
bool has_constant_value(const z3::expr &e, const u64 &val)
std::vector< std::vector< Net * > > reorder_commutative_operands(const std::vector< std::vector< Net * >> &operands, const std::vector< std::vector< Gate * >> &registers, const u32 permute_start_index=0)
Reorder commutative operands based on a permutation cache.
Definition: utils.cpp:194
const std::map< CandidateType, u32 > candidate_type_max_operands
A mapping of candidate types to their maximum number of operands.
CandidateType
Enumeration of the different candidate types for module identification.
@ addition_offset
Addition operation with a constant offset.
@ less_equal
Less-than-or-equal comparison.
@ value_check
Value check against a constant operation.
@ absolute
Absolute value operation.
@ constant_multiplication
Constant multiplication operation.
@ constant_multiplication_offset
Constant multiplication operation with a constant offset.
std::vector< Net * > get_output_nets(const std::vector< Gate * > &gates, bool only_external_destinations=true)
Get output nets from a list of gates.
Definition: utils.cpp:61
std::string join(const std::string &joiner, const Iterator &begin, const Iterator &end, const Transform &transform)
Definition: utils.h:414
std::vector< T > split(const T &s, const char delim, bool obey_brackets=false)
Definition: utils.h:239
Definition: defines.h:45
std::string enum_to_string(T e)
Definition: enums.h:53
Net * net
This file contains helper functions for module identification in the HAL framework.
QTextStream & bin(QTextStream &stream)
This struct manages the context of a candidate during module identification, including caches for all...
hal::Result< const std::set< Net * > > get_variable_nets(const Net *n, const std::map< Net *, BooleanFunction::Value > &ctrl_mapping)
Retrieves the variable nets for a given net and control mapping.
hal::Result< std::unordered_map< std::string, double > > get_boolean_influence(const Net *n, const std::map< Net *, BooleanFunction::Value > &ctrl_mapping)
Retrieves the boolean influence for a given net and control mapping.
const Netlist * m_netlist
The netlist associated with the candidate context.
hal::Result< const BooleanFunction > get_boolean_function(const Net *n, const std::map< Net *, BooleanFunction::Value > &ctrl_mapping)
Retrieves a boolean function for a given net and control mapping.
hal::Result< std::vector< BooleanFunction::Value > > evaluate(const Net *n, const std::map< Net *, BooleanFunction::Value > &ctrl_mapping, const std::map< std::string, BooleanFunction::Value > &eval_mapping)
Evaluates the boolean function for a given net, control mapping, and evaluation mapping.
hal::Result< const std::set< std::string > > get_variable_names(const Net *n, const std::map< Net *, BooleanFunction::Value > &ctrl_mapping)
Retrieves the variable names for a given net and control mapping.
The result of a module identification run containing the candidates.
Definition: result.h:55