HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
processing.cpp
Go to the documentation of this file.
2 
11 #include "hal_core/netlist/gate.h"
13 #include "hal_core/utilities/log.h"
14 
15 #include <iomanip>
16 #include <iostream>
17 #include <thread>
18 
19 namespace hal
20 {
21  namespace dataflow
22  {
23  namespace processing
24  {
25  namespace
26  {
27  ProgressPrinter m_progress_printer;
28 
29  void process_pass_configuration(const Configuration& config, Context& ctx)
30  {
31  u32 num_passes = ctx.current_passes.size();
32 
33  while (true)
34  {
35  u32 start_id;
36  u32 end_id;
37 
38  // fetch next work package
39  {
40  std::lock_guard<std::mutex> guard(ctx.progress_mutex);
41 
42  if (ctx.done)
43  {
44  break;
45  }
46 
47  // adaptive workload:
48  // every thread gets between 1 and 20 passes, depending on the number of remaining passes
49  // this improves cpu utilization and reduces number of mutex locks
50  start_id = ctx.pass_counter;
51  u32 remaining_passes = num_passes - start_id;
52 
53  u32 work = 20;
54 
55  if (remaining_passes < config.num_threads * work / 2)
56  {
57  work = std::max(1u, remaining_passes / config.num_threads);
58  }
59 
60  end_id = std::min(start_id + work, (u32)num_passes);
61 
62  ctx.pass_counter = end_id;
63  if (ctx.pass_counter >= num_passes)
64  {
65  std::lock_guard guard(ctx.result_mutex);
66  ctx.done = true;
67  }
68  }
69 
70  for (u32 current_id = start_id; current_id < end_id; ++current_id)
71  {
72  const auto& [current_state, current_pass] = ctx.current_passes[current_id];
73 
74  if (auto it = ctx.pass_outcome.find({current_state, current_pass.id}); it != ctx.pass_outcome.end())
75  {
76  {
77  // early exit, outcome is already known
78  std::lock_guard guard(ctx.result_mutex);
79  ctx.new_recurring_results.emplace_back(current_state, current_pass.id, it->second);
80  ctx.finished_passes++;
81  m_progress_printer.print_progress_to_stderr((float)ctx.finished_passes / ctx.current_passes.size(),
82  std::to_string(ctx.finished_passes) + "\\" + std::to_string(ctx.current_passes.size()) + " ("
83  + std::to_string(ctx.new_unique_groupings.size()) + " new results)");
84  }
85  m_progress_printer.print_progress_to_gui();
86  continue;
87  }
88 
89  // process work
90  auto new_state = current_pass.function(current_state);
91 
92  // aggregate result
93  std::shared_ptr<Grouping> duplicate = nullptr;
94  for (const auto& other : ctx.result.unique_groupings)
95  {
96  if (*new_state == *other)
97  {
98  duplicate = other;
99  break;
100  }
101  }
102  {
103  std::lock_guard guard(ctx.result_mutex);
104  if (duplicate == nullptr)
105  {
106  ctx.new_unique_groupings.emplace_back(current_state, current_pass.id, new_state);
107  }
108  else
109  {
110  ctx.new_recurring_results.emplace_back(current_state, current_pass.id, duplicate);
111  }
112 
113  ctx.finished_passes++;
114  m_progress_printer.print_progress_to_stderr((float)ctx.finished_passes / ctx.current_passes.size(),
115  std::to_string(ctx.finished_passes) + "\\" + std::to_string(ctx.current_passes.size()) + " ("
116  + std::to_string(ctx.new_unique_groupings.size()) + " new results)");
117  }
118  m_progress_printer.print_progress_to_gui();
119  }
120  }
121  }
122 
123  std::vector<std::pair<std::shared_ptr<Grouping>, PassConfiguration>>
124  generate_pass_combinations(Context& ctx, const Configuration& config, const std::shared_ptr<Grouping>& initial_grouping)
125  {
126  // create current layer of pass combinations;
127  std::vector<std::pair<std::shared_ptr<Grouping>, PassConfiguration>> output;
128 
129  if (initial_grouping != nullptr)
130  {
131  for (const auto& pass : pass_collection::get_passes(config, ctx.result.pass_combinations_leading_to_grouping[initial_grouping]))
132  {
133  output.emplace_back(initial_grouping, pass);
134  }
135  }
136  else
137  {
138  for (const auto& state : ctx.result.unique_groupings)
139  {
140  for (const auto& pass : pass_collection::get_passes(config, ctx.result.pass_combinations_leading_to_grouping[state]))
141  {
142  output.emplace_back(state, pass);
143  }
144  }
145  }
146 
147  return output;
148  }
149 
150  } // namespace
151 
152  Result run(const Configuration& config, const std::shared_ptr<Grouping>& initial_grouping)
153  {
154  log_info("dataflow", "starting pipeline with {} threads", config.num_threads);
155 
156  Context ctx;
157  ctx.num_iterations = 0;
158  ctx.phase = 0;
159  ctx.end_reached = false;
160 
161  for (u32 layer = 0; layer < config.pass_layers; layer++)
162  {
163  log_info("dataflow", "start processing layer {}", layer);
164  auto begin_time = std::chrono::high_resolution_clock::now();
165 
166  // get all pass combinations of layer
167  ctx.current_passes = generate_pass_combinations(ctx, config, (layer == 0) ? initial_grouping : nullptr);
168 
169  // preparations
170  ctx.done = false;
171  ctx.pass_counter = 0;
172  ctx.finished_passes = 0;
173 
174  m_progress_printer = ProgressPrinter(30);
175 
176  // spawn threads
177  std::vector<std::thread> workers;
178  for (u32 t = 0; t < config.num_threads - 1; ++t)
179  {
180  workers.emplace_back([&]() { process_pass_configuration(config, ctx); });
181  }
182 
183  process_pass_configuration(config, ctx);
184 
185  // wait for threads to finish
186  for (auto& worker : workers)
187  {
188  worker.join();
189  }
190 
191  m_progress_printer.clear();
192 
193  log_info("dataflow", " finished in {:3.2f}s, processed {} passes, filtering results...", seconds_since(begin_time), ctx.finished_passes, ctx.new_unique_groupings.size());
194 
195  auto all_new_results = ctx.new_recurring_results;
196 
197  begin_time = std::chrono::high_resolution_clock::now();
198 
199  // filter same results of different threads
200  u32 num_unique_filtered = 0;
201  std::vector<bool> do_not_consider(ctx.new_unique_groupings.size(), false);
202  for (u32 i = 0; i < ctx.new_unique_groupings.size(); ++i)
203  {
204  if (do_not_consider[i])
205  {
206  continue;
207  }
208  const auto& [start_state_i, pass_i, new_state_i] = ctx.new_unique_groupings[i];
209  for (u32 j = i + 1; j < ctx.new_unique_groupings.size(); ++j)
210  {
211  if (do_not_consider[j])
212  {
213  continue;
214  }
215  const auto& [start_state_j, pass_j, new_state_j] = ctx.new_unique_groupings[j];
216 
217  // j is a duplicate of i
218  if (*new_state_i == *new_state_j)
219  {
220  do_not_consider[j] = true;
221  all_new_results.emplace_back(start_state_j, pass_j, new_state_i);
222  }
223  }
224  ctx.result.unique_groupings.push_back(new_state_i);
225  all_new_results.push_back(ctx.new_unique_groupings[i]);
226  num_unique_filtered++;
227  }
228  log_info("dataflow", " filtered results in {:3.2f}s, got {} new unique results", seconds_since(begin_time), num_unique_filtered);
229 
230  begin_time = std::chrono::high_resolution_clock::now();
231  ctx.new_recurring_results.clear();
232  ctx.new_unique_groupings.clear();
233 
234  // fill results: compute path by appending pass id to the path of the prev round
235  for (const auto& [start_state, pass, new_state] : all_new_results)
236  {
237  ctx.pass_outcome[{start_state, pass}] = new_state;
238 
239  const auto& start_pass_combinations = ctx.result.pass_combinations_leading_to_grouping[start_state];
240  auto& new_pass_combinations = ctx.result.pass_combinations_leading_to_grouping[new_state];
241  if (start_pass_combinations.empty())
242  {
243  std::vector<pass_id> path{pass};
244  new_pass_combinations.push_back(path);
245  ctx.result.groupings[path] = new_state;
246  }
247  else
248  {
249  std::vector<std::vector<pass_id>> new_paths; // temporary memory to avoid modification while looping
250  new_paths.reserve(start_pass_combinations.size());
251  for (const auto& path : start_pass_combinations)
252  {
253  if (path.size() != layer)
254  {
255  continue;
256  }
257  std::vector<pass_id> new_path(path);
258  new_path.push_back(pass);
259  new_paths.push_back(new_path);
260  ctx.result.groupings[new_path] = new_state;
261  }
262  new_pass_combinations.insert(new_pass_combinations.end(), new_paths.begin(), new_paths.end());
263  }
264  }
265  log_info("dataflow", " total: {} unique states", ctx.result.unique_groupings.size());
266  }
267 
268  return ctx.result;
269  }
270 
271  void clear()
272  {
274  }
275  } // namespace processing
276  } // namespace dataflow
277 } // namespace hal
uint32_t u32
Definition: defines.h:41
#define log_info(channel,...)
Definition: log.h:70
std::vector< PassConfiguration > get_passes(const Configuration &config, const std::vector< std::vector< pass_id >> &previous_passes)
processing::Result run(const processing::Configuration &config, const std::shared_ptr< Grouping > &initial_grouping)
Definition: processing.cpp:152
Definition: defines.h:45
This file contains the struct that holds all information on the netlist abstraction used for dataflow...
This file contains the class that holds all information of a dataflow analysis grouping.
std::vector< std::pair< std::shared_ptr< Grouping >, PassConfiguration > > current_passes
Definition: context.h:76
std::vector< std::tuple< std::shared_ptr< Grouping >, pass_id, std::shared_ptr< Grouping > > > new_recurring_results
Definition: context.h:85
std::map< std::pair< std::shared_ptr< Grouping >, pass_id >, std::shared_ptr< Grouping > > pass_outcome
Definition: context.h:79
std::vector< std::tuple< std::shared_ptr< Grouping >, pass_id, std::shared_ptr< Grouping > > > new_unique_groupings
Definition: context.h:82
processing::Result result
Definition: context.h:98
std::map< std::shared_ptr< Grouping >, std::vector< std::vector< pass_id > > > pass_combinations_leading_to_grouping
Definition: result.h:52
std::map< std::vector< pass_id >, std::shared_ptr< Grouping > > groupings
Definition: result.h:55
std::vector< std::shared_ptr< Grouping > > unique_groupings
Definition: result.h:49
#define seconds_since(X)
Definition: timing_utils.h:38